feat(iaas): add iaas network v2 alpha (#899)
* add experimental network v2
This commit is contained in:
parent
a00b0466d5
commit
d9dc1d4495
28 changed files with 3777 additions and 923 deletions
|
|
@ -13,9 +13,11 @@ import (
|
|||
|
||||
const (
|
||||
RoutingTablesExperiment = "routing-tables"
|
||||
NetworkExperiment = "network"
|
||||
IamExperiment = "iam"
|
||||
)
|
||||
|
||||
var AvailableExperiments = []string{"iam", RoutingTablesExperiment}
|
||||
var AvailableExperiments = []string{IamExperiment, RoutingTablesExperiment, NetworkExperiment}
|
||||
|
||||
// Check if an experiment is valid.
|
||||
func ValidExperiment(experiment string, diags *diag.Diagnostics) bool {
|
||||
|
|
@ -31,11 +33,21 @@ func ValidExperiment(experiment string, diags *diag.Diagnostics) bool {
|
|||
|
||||
// Check if an experiment is enabled.
|
||||
func CheckExperimentEnabled(ctx context.Context, data *core.ProviderData, experiment, resourceName string, resourceType core.ResourceType, diags *diag.Diagnostics) {
|
||||
if CheckExperimentEnabledWithoutError(ctx, data, experiment, resourceName, resourceType, diags) {
|
||||
return
|
||||
}
|
||||
errTitle := fmt.Sprintf("%s is part of the %s experiment, which is currently disabled by default", resourceName, experiment)
|
||||
errContent := fmt.Sprintf(`Enable the %s experiment by adding it into your provider block.`, experiment)
|
||||
tflog.Error(ctx, fmt.Sprintf("%s | %s", errTitle, errContent))
|
||||
diags.AddError(errTitle, errContent)
|
||||
}
|
||||
|
||||
func CheckExperimentEnabledWithoutError(ctx context.Context, data *core.ProviderData, experiment, resourceName string, resourceType core.ResourceType, diags *diag.Diagnostics) bool {
|
||||
if !ValidExperiment(experiment, diags) {
|
||||
errTitle := fmt.Sprintf("The experiment %s does not exist.", experiment)
|
||||
errContent := "This is a bug in the STACKIT Terraform Provider. Please open an issue here: https://github.com/stackitcloud/terraform-provider-stackit/issues"
|
||||
diags.AddError(errTitle, errContent)
|
||||
return
|
||||
return false
|
||||
}
|
||||
experimentActive := slices.ContainsFunc(data.Experiments, func(e string) bool {
|
||||
return strings.EqualFold(e, experiment)
|
||||
|
|
@ -46,12 +58,9 @@ func CheckExperimentEnabled(ctx context.Context, data *core.ProviderData, experi
|
|||
warnContent := fmt.Sprintf("This %s is part of the %s experiment and is likely going to undergo significant changes or be removed in the future. Use it at your own discretion.", resourceType, experiment)
|
||||
tflog.Warn(ctx, fmt.Sprintf("%s | %s", warnTitle, warnContent))
|
||||
diags.AddWarning(warnTitle, warnContent)
|
||||
return
|
||||
return true
|
||||
}
|
||||
errTitle := fmt.Sprintf("%s is part of the %s experiment, which is currently disabled by default", resourceName, experiment)
|
||||
errContent := fmt.Sprintf(`Enable the %s experiment by adding it into your provider block.`, experiment)
|
||||
tflog.Error(ctx, fmt.Sprintf("%s | %s", errTitle, errContent))
|
||||
diags.AddError(errTitle, errContent)
|
||||
return false
|
||||
}
|
||||
|
||||
func AddExperimentDescription(description, experiment string, resourceType core.ResourceType) string {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func TestValidExperiment(t *testing.T) {
|
|||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
experiment: "iam",
|
||||
experiment: IamExperiment,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
want: true,
|
||||
|
|
@ -64,9 +64,9 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
|||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{"iam"},
|
||||
Experiments: []string{IamExperiment},
|
||||
},
|
||||
experiment: "iam",
|
||||
experiment: IamExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
|
|
@ -80,7 +80,7 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
|||
data: &core.ProviderData{
|
||||
Experiments: []string{},
|
||||
},
|
||||
experiment: "iam",
|
||||
experiment: IamExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
|
|
@ -92,7 +92,7 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
|||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{"iam"},
|
||||
Experiments: []string{IamExperiment},
|
||||
},
|
||||
experiment: "foobar",
|
||||
resourceType: core.Resource,
|
||||
|
|
@ -101,6 +101,34 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
|||
wantDiagsErr: true,
|
||||
wantDiagsWarning: false,
|
||||
},
|
||||
{
|
||||
name: "enabled multiple experiment",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment, NetworkExperiment, RoutingTablesExperiment},
|
||||
},
|
||||
experiment: NetworkExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantDiagsErr: false,
|
||||
wantDiagsWarning: true,
|
||||
},
|
||||
{
|
||||
name: "enabled multiple experiment - without the required experiment",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment, RoutingTablesExperiment},
|
||||
},
|
||||
experiment: NetworkExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantDiagsErr: true,
|
||||
wantDiagsWarning: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -114,3 +142,111 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckExperimentEnabledWithoutError(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
data *core.ProviderData
|
||||
experiment string
|
||||
resourceName string
|
||||
resourceType core.ResourceType
|
||||
diags *diag.Diagnostics
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantEnabled bool
|
||||
wantDiagsErr bool
|
||||
wantDiagsWarning bool
|
||||
}{
|
||||
|
||||
{
|
||||
name: "enabled",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment},
|
||||
},
|
||||
experiment: IamExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantEnabled: true,
|
||||
wantDiagsErr: false,
|
||||
wantDiagsWarning: true,
|
||||
},
|
||||
{
|
||||
name: "disabled - no error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{},
|
||||
},
|
||||
experiment: NetworkExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantEnabled: false,
|
||||
wantDiagsErr: false,
|
||||
wantDiagsWarning: false,
|
||||
},
|
||||
{
|
||||
name: "invalid experiment",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment},
|
||||
},
|
||||
experiment: "foobar",
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantEnabled: false,
|
||||
wantDiagsErr: true,
|
||||
wantDiagsWarning: false,
|
||||
},
|
||||
{
|
||||
name: "enabled multiple experiment",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment, NetworkExperiment, RoutingTablesExperiment},
|
||||
},
|
||||
experiment: NetworkExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantEnabled: true,
|
||||
wantDiagsErr: false,
|
||||
wantDiagsWarning: true,
|
||||
},
|
||||
{
|
||||
name: "enabled multiple experiment - without the required experiment",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: &core.ProviderData{
|
||||
Experiments: []string{IamExperiment, RoutingTablesExperiment},
|
||||
},
|
||||
experiment: NetworkExperiment,
|
||||
resourceType: core.Resource,
|
||||
diags: &diag.Diagnostics{},
|
||||
},
|
||||
wantEnabled: false,
|
||||
wantDiagsErr: false,
|
||||
wantDiagsWarning: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := CheckExperimentEnabledWithoutError(tt.args.ctx, tt.args.data, tt.args.experiment, tt.args.resourceName, tt.args.resourceType, tt.args.diags); got != tt.wantEnabled {
|
||||
t.Errorf("CheckExperimentEnabledWithoutError() = %v, want %v", got, tt.wantEnabled)
|
||||
}
|
||||
if got := tt.args.diags.HasError(); got != tt.wantDiagsErr {
|
||||
t.Errorf("CheckExperimentEnabled() diags.HasError() = %v, want %v", got, tt.wantDiagsErr)
|
||||
}
|
||||
if got := tt.args.diags.WarningsCount() > 0; got != tt.wantDiagsWarning {
|
||||
t.Errorf("CheckExperimentEnabled() diags.WarningsCount() > 0 = %v, want %v", got, tt.wantDiagsErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ var roleTargets = []string{
|
|||
"organization",
|
||||
}
|
||||
|
||||
// This resource is part of the "iam" experiment
|
||||
var experiment = "iam"
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &roleAssignmentResource{}
|
||||
|
|
@ -84,7 +81,7 @@ func (r *roleAssignmentResource) Configure(ctx context.Context, req resource.Con
|
|||
return
|
||||
}
|
||||
|
||||
features.CheckExperimentEnabled(ctx, &providerData, experiment, fmt.Sprintf("stackit_authorization_%s_role_assignment", r.apiName), core.Resource, &resp.Diagnostics)
|
||||
features.CheckExperimentEnabled(ctx, &providerData, features.IamExperiment, fmt.Sprintf("stackit_authorization_%s_role_assignment", r.apiName), core.Resource, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
|
@ -100,7 +97,7 @@ func (r *roleAssignmentResource) Configure(ctx context.Context, req resource.Con
|
|||
// Schema defines the schema for the resource.
|
||||
func (r *roleAssignmentResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": features.AddExperimentDescription(fmt.Sprintf("%s Role Assignment resource schema.", r.apiName), experiment, core.Resource),
|
||||
"main": features.AddExperimentDescription(fmt.Sprintf("%s Role Assignment resource schema.", r.apiName), features.IamExperiment, core.Resource),
|
||||
"id": "Terraform's internal resource identifier. It is structured as \"[resource_id],[role],[subject]\".",
|
||||
"resource_id": fmt.Sprintf("%s Resource to assign the role to.", r.apiName),
|
||||
"role": "Role to be assigned",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -21,6 +22,8 @@ import (
|
|||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas/wait"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
waitAlpha "github.com/stackitcloud/stackit-sdk-go/services/iaasalpha/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||
)
|
||||
|
|
@ -50,11 +53,17 @@ var (
|
|||
//go:embed testdata/resource-network-area-max.tf
|
||||
resourceNetworkAreaMaxConfig string
|
||||
|
||||
//go:embed testdata/resource-network-min.tf
|
||||
resourceNetworkMinConfig string
|
||||
//go:embed testdata/resource-network-v1-min.tf
|
||||
resourceNetworkV1MinConfig string
|
||||
|
||||
//go:embed testdata/resource-network-max.tf
|
||||
resourceNetworkMaxConfig string
|
||||
//go:embed testdata/resource-network-v1-max.tf
|
||||
resourceNetworkV1MaxConfig string
|
||||
|
||||
//go:embed testdata/resource-network-v2-min.tf
|
||||
resourceNetworkV2MinConfig string
|
||||
|
||||
//go:embed testdata/resource-network-v2-max.tf
|
||||
resourceNetworkV2MaxConfig string
|
||||
|
||||
//go:embed testdata/resource-network-interface-min.tf
|
||||
resourceNetworkInterfaceMinConfig string
|
||||
|
|
@ -83,21 +92,10 @@ var (
|
|||
|
||||
const (
|
||||
keypairPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIDsPd27M449akqCtdFg2+AmRVJz6eWio0oMP9dVg7XZ"
|
||||
// TODO: create network area using terraform resource instead once it's out of experimental stage and GA
|
||||
testNetworkAreaId = "25bbf23a-8134-4439-9f5e-1641caf8354e"
|
||||
)
|
||||
|
||||
// Network resource data
|
||||
var networkResource = map[string]string{
|
||||
"project_id": testutil.ProjectId,
|
||||
"name": fmt.Sprintf("acc-test-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
|
||||
"ipv4_prefix_length": "24",
|
||||
"nameserver0": "1.2.3.4",
|
||||
"nameserver1": "5.6.7.8",
|
||||
"ipv4_gateway": "10.2.2.1",
|
||||
"ipv4_prefix": "10.2.2.0/24",
|
||||
"routed": "false",
|
||||
"name_updated": fmt.Sprintf("acc-test-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)),
|
||||
}
|
||||
|
||||
var testConfigServerVarsMin = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||
|
|
@ -225,31 +223,67 @@ var testConfigVolumeVarsMaxUpdated = func() config.Variables {
|
|||
return updatedConfig
|
||||
}()
|
||||
|
||||
var testConfigNetworkVarsMin = config.Variables{
|
||||
var testConfigNetworkV1VarsMin = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||
}
|
||||
|
||||
var testConfigNetworkVarsMax = config.Variables{
|
||||
var testConfigNetworkV1VarsMax = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||
"ipv4_gateway": config.StringVariable("10.2.2.1"),
|
||||
"ipv4_nameservers": config.StringVariable("10.2.2.2"),
|
||||
"ipv4_nameserver_0": config.StringVariable("10.2.2.2"),
|
||||
"ipv4_nameserver_1": config.StringVariable("10.2.2.3"),
|
||||
"ipv4_prefix": config.StringVariable("10.2.2.0/24"),
|
||||
"ipv4_prefix_length": config.IntegerVariable(24),
|
||||
"routed": config.BoolVariable(false),
|
||||
"label": config.StringVariable("label"),
|
||||
}
|
||||
|
||||
var testConfigNetworkVarsMaxUpdated = func() config.Variables {
|
||||
var testConfigNetworkV1VarsMaxUpdated = func() config.Variables {
|
||||
updatedConfig := config.Variables{}
|
||||
for k, v := range testConfigNetworkVarsMax {
|
||||
for k, v := range testConfigNetworkV1VarsMax {
|
||||
updatedConfig[k] = v
|
||||
}
|
||||
updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"])))
|
||||
updatedConfig["ipv4_gateway"] = config.StringVariable("")
|
||||
updatedConfig["ipv4_nameservers"] = config.StringVariable("10.2.2.3")
|
||||
updatedConfig["ipv4_prefix"] = config.StringVariable("10.2.2.0/25")
|
||||
updatedConfig["ipv4_prefix_length"] = config.IntegerVariable(25)
|
||||
updatedConfig["ipv4_nameserver_0"] = config.StringVariable("10.2.2.10")
|
||||
updatedConfig["label"] = config.StringVariable("updated")
|
||||
return updatedConfig
|
||||
}()
|
||||
|
||||
var testConfigNetworkV2VarsMin = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||
}
|
||||
|
||||
var testConfigNetworkV2VarsMinUpdated = func() config.Variables {
|
||||
updatedConfig := config.Variables{}
|
||||
maps.Copy(updatedConfig, testConfigNetworkV2VarsMin)
|
||||
updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"])))
|
||||
return updatedConfig
|
||||
}()
|
||||
|
||||
var testConfigNetworkV2VarsMax = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||
"ipv4_gateway": config.StringVariable("10.2.2.1"),
|
||||
"ipv4_nameserver_0": config.StringVariable("10.2.2.2"),
|
||||
"ipv4_nameserver_1": config.StringVariable("10.2.2.3"),
|
||||
"ipv4_prefix": config.StringVariable("10.2.2.0/24"),
|
||||
"ipv4_prefix_length": config.IntegerVariable(24),
|
||||
"routed": config.BoolVariable(true),
|
||||
"label": config.StringVariable("label"),
|
||||
"organization_id": config.StringVariable(testutil.OrganizationId),
|
||||
"network_area_id": config.StringVariable(testNetworkAreaId),
|
||||
}
|
||||
|
||||
var testConfigNetworkV2VarsMaxUpdated = func() config.Variables {
|
||||
updatedConfig := config.Variables{}
|
||||
maps.Copy(updatedConfig, testConfigNetworkV2VarsMax)
|
||||
updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"])))
|
||||
updatedConfig["ipv4_gateway"] = config.StringVariable("")
|
||||
updatedConfig["ipv4_nameserver_0"] = config.StringVariable("10.2.2.10")
|
||||
updatedConfig["label"] = config.StringVariable("updated")
|
||||
return updatedConfig
|
||||
}()
|
||||
|
|
@ -456,20 +490,20 @@ var testConfigKeyPairMaxUpdated = func() config.Variables {
|
|||
// if no local file is provided the test should create a default file and work with this instead of failing
|
||||
var localFileForIaasImage os.File
|
||||
|
||||
func TestAccNetworkMin(t *testing.T) {
|
||||
t.Logf("TestAccNetworkMin name: %s", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"]))
|
||||
func TestAccNetworkV1Min(t *testing.T) {
|
||||
t.Logf("TestAccNetworkV1Min name: %s", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"]))
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMinConfig),
|
||||
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkV1MinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||
|
|
@ -477,7 +511,7 @@ func TestAccNetworkMin(t *testing.T) {
|
|||
},
|
||||
// Data source
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMin,
|
||||
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
|
@ -487,12 +521,12 @@ func TestAccNetworkMin(t *testing.T) {
|
|||
network_id = stackit_network.network.network_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceNetworkMinConfig,
|
||||
testutil.IaaSProviderConfig(), resourceNetworkV1MinConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "public_ip"),
|
||||
|
|
@ -501,7 +535,7 @@ func TestAccNetworkMin(t *testing.T) {
|
|||
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMin,
|
||||
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||
ResourceName: "stackit_network.network",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_network.network"]
|
||||
|
|
@ -517,8 +551,8 @@ func TestAccNetworkMin(t *testing.T) {
|
|||
ImportState: true,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||
|
|
@ -530,70 +564,106 @@ func TestAccNetworkMin(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccNetworkMax(t *testing.T) {
|
||||
t.Logf("TestAccNetworkMax name: %s", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"]))
|
||||
func TestAccNetworkV1Max(t *testing.T) {
|
||||
t.Logf("TestAccNetworkV1Max name: %s", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"]))
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMaxConfig),
|
||||
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkV1MaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_gateway"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network", "no_ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_nameservers"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network", "public_ip")),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "no_ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "public_ip"),
|
||||
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "public_ip"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMax,
|
||||
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_network" "network" {
|
||||
project_id = stackit_network.network.project_id
|
||||
network_id = stackit_network.network.network_id
|
||||
data "stackit_network" "network_prefix" {
|
||||
project_id = stackit_network.network_prefix.project_id
|
||||
network_id = stackit_network.network_prefix.network_id
|
||||
}
|
||||
|
||||
data "stackit_network" "network_prefix_length" {
|
||||
project_id = stackit_network.network_prefix_length.project_id
|
||||
network_id = stackit_network.network_prefix_length.network_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceNetworkMaxConfig,
|
||||
testutil.IaaSProviderConfig(), resourceNetworkV1MaxConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_gateway"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_nameservers"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["label"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["label"])),
|
||||
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["label"])),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMax,
|
||||
ResourceName: "stackit_network.network",
|
||||
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||
ResourceName: "stackit_network.network_prefix",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_network.network"]
|
||||
r, ok := s.RootModule().Resources["stackit_network.network_prefix"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_network.network")
|
||||
return "", fmt.Errorf("couldn't find resource stackit_network.network_prefix")
|
||||
}
|
||||
networkId, ok := r.Primary.Attributes["network_id"]
|
||||
if !ok {
|
||||
|
|
@ -603,39 +673,449 @@ func TestAccNetworkMax(t *testing.T) {
|
|||
},
|
||||
ImportState: true,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", networkResource["name"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_gateway", networkResource["ipv4_gateway"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
// nameservers may be returned in a randomized order, so we have to check them with a helper function
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network", "nameservers.*", networkResource["nameserver0"]),
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network", "nameservers.*", networkResource["nameserver1"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix", networkResource["ipv4_prefix"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix_length", networkResource["ipv4_prefix_length"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.0", networkResource["ipv4_prefix"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "routed", networkResource["routed"]),
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
),
|
||||
},
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||
ResourceName: "stackit_network.network_prefix_length",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_network.network_prefix_length"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_network.network_prefix_length")
|
||||
}
|
||||
networkId, ok := r.Primary.Attributes["network_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute network_id")
|
||||
}
|
||||
return fmt.Sprintf("%s,%s", testutil.ProjectId, networkId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
// nameservers may be returned in a randomized order, so we have to check them with a helper function
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix_length", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix_length", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||
),
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigNetworkVarsMaxUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMaxConfig),
|
||||
ConfigVariables: testConfigNetworkV1VarsMaxUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkV1MaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["name"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "public_ip"),
|
||||
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["name"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMaxUpdated["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "public_ip"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccNetworkV2Min(t *testing.T) {
|
||||
t.Logf("TestAccNetworkV2Min name: %s", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["name"]))
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckNetworkV2Destroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["name"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["ipv4_nameservers"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["ipv4_prefix"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["label"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_network.network", "public_ip"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "region"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "routing_table_id"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMin,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_network" "network" {
|
||||
project_id = stackit_network.network.project_id
|
||||
network_id = stackit_network.network.network_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MinConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "public_ip"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "region"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "routing_table_id"),
|
||||
),
|
||||
},
|
||||
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMin,
|
||||
ResourceName: "stackit_network.network",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_network.network"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_network.network")
|
||||
}
|
||||
region, ok := r.Primary.Attributes["region"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute region")
|
||||
}
|
||||
networkId, ok := r.Primary.Attributes["network_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute network_id")
|
||||
}
|
||||
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, region, networkId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["name"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "region"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "routing_table_id"),
|
||||
),
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMinUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMinUpdated["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMinUpdated["name"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "region"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network", "routing_table_id"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccNetworkV2Max(t *testing.T) {
|
||||
t.Logf("TestAccNetworkV2Max name: %s", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"]))
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckNetworkV2Destroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// TODO: enable test cases for prefix option, when the API works again
|
||||
// Network with prefix
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_gateway"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "no_ipv4_gateway"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["label"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "public_ip"),
|
||||
|
||||
// Network with prefix_length
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["label"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "public_ip"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "region", testutil.Region),
|
||||
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_network.network_prefix_length", "routing_table_id",
|
||||
"stackit_routing_table.routing_table", "routing_table_id",
|
||||
),
|
||||
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["organization_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "network_area_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["network_area_id"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "routing_table_id"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "labels.%", "0"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "region", testutil.Region),
|
||||
resource.TestCheckNoResourceAttr("stackit_routing_table.routing_table", "description"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "system_routes", "true"),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "updated_at"),
|
||||
),
|
||||
},
|
||||
// Data source
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMax,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
//data "stackit_network" "network_prefix" {
|
||||
// project_id = stackit_network.network_prefix.project_id
|
||||
// network_id = stackit_network.network_prefix.network_id
|
||||
//}
|
||||
|
||||
data "stackit_network" "network_prefix_length" {
|
||||
project_id = stackit_network.network_prefix_length.project_id
|
||||
network_id = stackit_network.network_prefix_length.network_id
|
||||
}
|
||||
|
||||
data "stackit_routing_table" "routing_table" {
|
||||
organization_id = stackit_routing_table.routing_table.organization_id
|
||||
network_area_id = stackit_routing_table.routing_table.network_area_id
|
||||
routing_table_id = stackit_routing_table.routing_table.routing_table_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MaxConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// TODO: enable test cases for prefix option, when the API works again
|
||||
// Network with prefix
|
||||
// resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "network_id"),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_gateway"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
// resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
// resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
// resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
// resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["label"])),
|
||||
|
||||
// Network with prefix_length
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
// resource.TestCheckNoResourceAttr("data.stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix_length", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["label"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_network.network_prefix_length", "region", testutil.Region),
|
||||
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"data.stackit_network.network_prefix_length", "routing_table_id",
|
||||
"data.stackit_routing_table.routing_table", "routing_table_id",
|
||||
),
|
||||
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["organization_id"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "network_area_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["network_area_id"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_routing_table.routing_table", "routing_table_id"),
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "labels.%", "0"),
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "region", testutil.Region),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_routing_table.routing_table", "description"),
|
||||
resource.TestCheckResourceAttr("data.stackit_routing_table.routing_table", "system_routes", "true"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_routing_table.routing_table", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_routing_table.routing_table", "updated_at"),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
// TODO: enable test cases for prefix option, when the API works again
|
||||
//{
|
||||
// ConfigVariables: testConfigNetworkV2VarsMax,
|
||||
// ResourceName: "stackit_network.network_prefix",
|
||||
// ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
// r, ok := s.RootModule().Resources["stackit_network.network_prefix"]
|
||||
// if !ok {
|
||||
// return "", fmt.Errorf("couldn't find resource stackit_network.network_prefix")
|
||||
// }
|
||||
// networkId, ok := r.Primary.Attributes["network_id"]
|
||||
// if !ok {
|
||||
// return "", fmt.Errorf("couldn't find attribute network_id")
|
||||
// }
|
||||
// return fmt.Sprintf("%s,%s", testutil.ProjectId, networkId), nil
|
||||
// },
|
||||
// ImportState: true,
|
||||
// Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_gateway"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
// // nameservers may be returned in a randomized order, so we have to check them with a helper function
|
||||
// resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
// resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.0", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMax,
|
||||
ResourceName: "stackit_network.network_prefix_length",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_network.network_prefix_length"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_network.network_prefix_length")
|
||||
}
|
||||
region, ok := r.Primary.Attributes["region"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute region")
|
||||
}
|
||||
networkId, ok := r.Primary.Attributes["network_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute network_id")
|
||||
}
|
||||
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, region, networkId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["project_id"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
// nameservers may be returned in a randomized order, so we have to check them with a helper function
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix_length", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_0"])),
|
||||
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix_length", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefixes.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMax["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "region", testutil.Region),
|
||||
),
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigNetworkV2VarsMaxUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceNetworkV2MaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// TODO: enable test cases for prefix option, when the API works again
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["project_id"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["name"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "ipv4_gateway"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "no_ipv4_gateway", "true"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_nameserver_0"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_nameserver_1"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_prefix"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_prefix_length"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||
// resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["routed"])),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["label"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "public_ip"),
|
||||
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "network_id"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["project_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["name"])),
|
||||
// resource.TestCheckNoResourceAttr("stackit_network.network_prefix_length", "ipv4_gateway"),
|
||||
// resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "no_ipv4_gateway", "true"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.#", "2"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_nameserver_0"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_nameserver_1"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["ipv4_prefix_length"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv4_prefix"),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "ipv6_prefixes.#"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "routed", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["routed"])),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["label"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_network.network_prefix_length", "public_ip"),
|
||||
resource.TestCheckResourceAttr("stackit_network.network_prefix_length", "region", testutil.Region),
|
||||
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_network.network_prefix_length", "routing_table_id",
|
||||
"stackit_routing_table.routing_table", "routing_table_id",
|
||||
),
|
||||
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["organization_id"])),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "network_area_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["network_area_id"])),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "routing_table_id"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMaxUpdated["name"])),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "labels.%", "0"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "region", testutil.Region),
|
||||
resource.TestCheckNoResourceAttr("stackit_routing_table.routing_table", "description"),
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "system_routes", "true"),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "created_at"),
|
||||
resource.TestCheckResourceAttrSet("stackit_routing_table.routing_table", "updated_at"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
|
|
@ -3544,7 +4024,7 @@ func TestAccImageMax(t *testing.T) {
|
|||
|
||||
func testAccCheckDestroy(s *terraform.State) error {
|
||||
checkFunctions := []func(s *terraform.State) error{
|
||||
testAccCheckNetworkDestroy,
|
||||
testAccCheckNetworkV1Destroy,
|
||||
testAccCheckNetworkInterfaceDestroy,
|
||||
testAccCheckNetworkAreaDestroy,
|
||||
testAccCheckIaaSVolumeDestroy,
|
||||
|
|
@ -3573,7 +4053,7 @@ func testAccCheckDestroy(s *terraform.State) error {
|
|||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func testAccCheckNetworkDestroy(s *terraform.State) error {
|
||||
func testAccCheckNetworkV1Destroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *iaas.APIClient
|
||||
var err error
|
||||
|
|
@ -3616,6 +4096,48 @@ func testAccCheckNetworkDestroy(s *terraform.State) error {
|
|||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func testAccCheckNetworkV2Destroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *iaasalpha.APIClient
|
||||
var err error
|
||||
if testutil.IaaSCustomEndpoint == "" {
|
||||
client, err = iaasalpha.NewAPIClient()
|
||||
} else {
|
||||
client, err = iaasalpha.NewAPIClient(
|
||||
stackitSdkConfig.WithEndpoint(testutil.IaaSCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
// networks
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_network" {
|
||||
continue
|
||||
}
|
||||
region := strings.Split(rs.Primary.ID, core.Separator)[1]
|
||||
networkId := strings.Split(rs.Primary.ID, core.Separator)[2]
|
||||
err := client.DeleteNetworkExecute(ctx, testutil.ProjectId, region, networkId)
|
||||
if err != nil {
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
if errors.As(err, &oapiErr) {
|
||||
if oapiErr.StatusCode == http.StatusNotFound {
|
||||
continue
|
||||
}
|
||||
}
|
||||
errs = append(errs, fmt.Errorf("cannot trigger network deletion %q: %w", networkId, err))
|
||||
}
|
||||
_, err = waitAlpha.DeleteNetworkWaitHandler(ctx, client, testutil.ProjectId, region, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("cannot delete network %q: %w", networkId, err))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func testAccCheckNetworkInterfaceDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *iaas.APIClient
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ package network
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/v1network"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/v2network"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
iaasAlphaUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
|
|
@ -17,7 +19,6 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
|
|
@ -26,28 +27,6 @@ var (
|
|||
_ datasource.DataSource = &networkDataSource{}
|
||||
)
|
||||
|
||||
type DataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
NetworkId types.String `tfsdk:"network_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Nameservers types.List `tfsdk:"nameservers"`
|
||||
IPv4Gateway types.String `tfsdk:"ipv4_gateway"`
|
||||
IPv4Nameservers types.List `tfsdk:"ipv4_nameservers"`
|
||||
IPv4Prefix types.String `tfsdk:"ipv4_prefix"`
|
||||
IPv4PrefixLength types.Int64 `tfsdk:"ipv4_prefix_length"`
|
||||
Prefixes types.List `tfsdk:"prefixes"`
|
||||
IPv4Prefixes types.List `tfsdk:"ipv4_prefixes"`
|
||||
IPv6Gateway types.String `tfsdk:"ipv6_gateway"`
|
||||
IPv6Nameservers types.List `tfsdk:"ipv6_nameservers"`
|
||||
IPv6Prefix types.String `tfsdk:"ipv6_prefix"`
|
||||
IPv6PrefixLength types.Int64 `tfsdk:"ipv6_prefix_length"`
|
||||
IPv6Prefixes types.List `tfsdk:"ipv6_prefixes"`
|
||||
PublicIP types.String `tfsdk:"public_ip"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
Routed types.Bool `tfsdk:"routed"`
|
||||
}
|
||||
|
||||
// NewNetworkDataSource is a helper function to simplify the provider implementation.
|
||||
func NewNetworkDataSource() datasource.DataSource {
|
||||
return &networkDataSource{}
|
||||
|
|
@ -56,6 +35,10 @@ func NewNetworkDataSource() datasource.DataSource {
|
|||
// networkDataSource is the data source implementation.
|
||||
type networkDataSource struct {
|
||||
client *iaas.APIClient
|
||||
// alphaClient will be used in case the experimental flag "network" is set
|
||||
alphaClient *iaasalpha.APIClient
|
||||
isExperimental bool
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
|
|
@ -64,16 +47,30 @@ func (d *networkDataSource) Metadata(_ context.Context, req datasource.MetadataR
|
|||
}
|
||||
|
||||
func (d *networkDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := iaasUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
d.isExperimental = features.CheckExperimentEnabledWithoutError(ctx, &d.providerData, features.NetworkExperiment, "stackit_network", core.Datasource, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
|
||||
if d.isExperimental {
|
||||
alphaApiClient := iaasAlphaUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.alphaClient = alphaApiClient
|
||||
} else {
|
||||
apiClient := iaasUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
}
|
||||
tflog.Info(ctx, "IaaS client configured")
|
||||
}
|
||||
|
||||
|
|
@ -181,193 +178,28 @@ func (d *networkDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
|
|||
Description: "Shows if the network is routed and therefore accessible from other networks.",
|
||||
Computed: true,
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
// the region cannot be found, so it has to be passed
|
||||
Optional: true,
|
||||
Description: "Can only be used when experimental \"network\" is set. This is likely going to undergo significant changes or be removed in the future.\nThe resource region. If not defined, the provider region is used.",
|
||||
},
|
||||
"routing_table_id": schema.StringAttribute{
|
||||
Description: "Can only be used when experimental \"network\" is set. This is likely going to undergo significant changes or be removed in the future. Use it at your own discretion.\nThe ID of the routing table associated with the network.",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (d *networkDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model DataSourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
if !d.isExperimental {
|
||||
v1network.DatasourceRead(ctx, req, resp, d.client)
|
||||
} else {
|
||||
v2network.DatasourceRead(ctx, req, resp, d.alphaClient, d.providerData)
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
networkResp, err := d.client.GetNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading network",
|
||||
fmt.Sprintf("Network with ID %q does not exist in project %q.", networkId, projectId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
err = mapDataSourceFields(ctx, networkResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(ctx context.Context, networkResp *iaas.Network, model *DataSourceModel) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.NetworkId != nil {
|
||||
networkId = *networkResp.NetworkId
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
|
||||
if networkResp.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
if networkResp.Gateway != nil {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.GetGateway())
|
||||
} else {
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.NameserversV6 == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.NameserversV6
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.PrefixesV6 == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.PrefixesV6
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
if networkResp.Gatewayv6 != nil {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.GetGatewayv6())
|
||||
} else {
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.PublicIP = types.StringPointerValue(networkResp.PublicIp)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,31 +2,30 @@ package network
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas/wait"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/v1network"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/v2network"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
iaasAlphaUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
|
@ -38,30 +37,6 @@ var (
|
|||
_ resource.ResourceWithImportState = &networkResource{}
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
NetworkId types.String `tfsdk:"network_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Nameservers types.List `tfsdk:"nameservers"`
|
||||
IPv4Gateway types.String `tfsdk:"ipv4_gateway"`
|
||||
IPv4Nameservers types.List `tfsdk:"ipv4_nameservers"`
|
||||
IPv4Prefix types.String `tfsdk:"ipv4_prefix"`
|
||||
IPv4PrefixLength types.Int64 `tfsdk:"ipv4_prefix_length"`
|
||||
Prefixes types.List `tfsdk:"prefixes"`
|
||||
IPv4Prefixes types.List `tfsdk:"ipv4_prefixes"`
|
||||
IPv6Gateway types.String `tfsdk:"ipv6_gateway"`
|
||||
IPv6Nameservers types.List `tfsdk:"ipv6_nameservers"`
|
||||
IPv6Prefix types.String `tfsdk:"ipv6_prefix"`
|
||||
IPv6PrefixLength types.Int64 `tfsdk:"ipv6_prefix_length"`
|
||||
IPv6Prefixes types.List `tfsdk:"ipv6_prefixes"`
|
||||
PublicIP types.String `tfsdk:"public_ip"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
Routed types.Bool `tfsdk:"routed"`
|
||||
NoIPv4Gateway types.Bool `tfsdk:"no_ipv4_gateway"`
|
||||
NoIPv6Gateway types.Bool `tfsdk:"no_ipv6_gateway"`
|
||||
}
|
||||
|
||||
// NewNetworkResource is a helper function to simplify the provider implementation.
|
||||
func NewNetworkResource() resource.Resource {
|
||||
return &networkResource{}
|
||||
|
|
@ -70,6 +45,10 @@ func NewNetworkResource() resource.Resource {
|
|||
// networkResource is the resource implementation.
|
||||
type networkResource struct {
|
||||
client *iaas.APIClient
|
||||
// alphaClient will be used in case the experimental flag "network" is set
|
||||
alphaClient *iaasalpha.APIClient
|
||||
isExperimental bool
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
|
|
@ -79,29 +58,85 @@ func (r *networkResource) Metadata(_ context.Context, req resource.MetadataReque
|
|||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *networkResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := iaasUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
r.isExperimental = features.CheckExperimentEnabledWithoutError(ctx, &r.providerData, features.NetworkExperiment, "stackit_network", core.Resource, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
|
||||
if r.isExperimental {
|
||||
alphaApiClient := iaasAlphaUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
r.alphaClient = alphaApiClient
|
||||
} else {
|
||||
apiClient := iaasUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
}
|
||||
tflog.Info(ctx, "IaaS client configured")
|
||||
}
|
||||
|
||||
func (r networkResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
|
||||
var model Model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
|
||||
// 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() {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if !model.Nameservers.IsUnknown() && !model.IPv4Nameservers.IsUnknown() && !model.Nameservers.IsNull() && !model.IPv4Nameservers.IsNull() {
|
||||
var planModel model.Model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *networkResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
|
||||
var resourceModel model.Model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &resourceModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if !resourceModel.Nameservers.IsUnknown() && !resourceModel.IPv4Nameservers.IsUnknown() && !resourceModel.Nameservers.IsNull() && !resourceModel.IPv4Nameservers.IsNull() {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring network", "You cannot provide both the `nameservers` and `ipv4_nameservers` fields simultaneously. Please remove the deprecated `nameservers` field, and use `ipv4_nameservers` to configure nameservers for IPv4.")
|
||||
}
|
||||
if !r.isExperimental {
|
||||
if !utils.IsUndefined(resourceModel.Region) {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring network", "Setting the `region` is not supported yet. This can only be configured when the experiments `network` is set.")
|
||||
}
|
||||
if !utils.IsUndefined(resourceModel.RoutingTableID) {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring network", "Setting the field `routing_table_id` is not supported yet. This can only be configured when the experiments `network` is set.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigValidators validates the resource configuration
|
||||
|
|
@ -115,6 +150,22 @@ func (r *networkResource) ConfigValidators(_ context.Context) []resource.ConfigV
|
|||
path.MatchRoot("no_ipv6_gateway"),
|
||||
path.MatchRoot("ipv6_gateway"),
|
||||
),
|
||||
resourcevalidator.Conflicting(
|
||||
path.MatchRoot("ipv4_prefix"),
|
||||
path.MatchRoot("ipv4_prefix_length"),
|
||||
),
|
||||
resourcevalidator.Conflicting(
|
||||
path.MatchRoot("ipv6_prefix"),
|
||||
path.MatchRoot("ipv6_prefix_length"),
|
||||
),
|
||||
resourcevalidator.Conflicting(
|
||||
path.MatchRoot("ipv4_prefix_length"),
|
||||
path.MatchRoot("ipv4_gateway"),
|
||||
),
|
||||
resourcevalidator.Conflicting(
|
||||
path.MatchRoot("ipv6_prefix_length"),
|
||||
path.MatchRoot("ipv6_gateway"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,13 +247,16 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
validate.CIDR(),
|
||||
},
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.RequiresReplaceIfConfigured(),
|
||||
},
|
||||
},
|
||||
"ipv4_prefix_length": schema.Int64Attribute{
|
||||
Description: "The IPv4 prefix length of the network.",
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.RequiresReplaceIfConfigured(),
|
||||
},
|
||||
},
|
||||
"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.",
|
||||
|
|
@ -285,498 +339,73 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
|||
boolplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"routing_table_id": schema.StringAttribute{
|
||||
Description: "Can only be used when experimental \"network\" is set.\nThe ID of the routing table associated with the network.",
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
// must be computed to allow for storing the override value from the provider
|
||||
Computed: true,
|
||||
Description: "Can only be used when experimental \"network\" is set.\nThe resource region. If not defined, the provider region is used.",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplaceIfConfigured(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 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 model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
if !r.isExperimental {
|
||||
v1network.Create(ctx, req, resp, r.client)
|
||||
} else {
|
||||
v2network.Create(ctx, req, resp, r.alphaClient)
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Create new network
|
||||
|
||||
network, err := r.client.CreateNetwork(ctx, projectId).CreateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
networkId := *network.NetworkId
|
||||
network, err = wait.CreateNetworkWaitHandler(ctx, r.client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Network creation waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, network, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network created")
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *networkResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
if !r.isExperimental {
|
||||
v1network.Read(ctx, req, resp, r.client)
|
||||
} else {
|
||||
v2network.Read(ctx, req, resp, r.alphaClient, r.providerData)
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
networkResp, err := r.client.GetNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, networkResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
// Update updates the resource and sets the updated Terraform state on success.
|
||||
func (r *networkResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
if !r.isExperimental {
|
||||
v1network.Update(ctx, req, resp, r.client)
|
||||
} else {
|
||||
v2network.Update(ctx, req, resp, r.alphaClient)
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Retrieve values from state
|
||||
var stateModel Model
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(ctx, &model, &stateModel)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing network
|
||||
err = r.client.PartialUpdateNetwork(ctx, projectId, networkId).PartialUpdateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.UpdateNetworkWaitHandler(ctx, r.client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Network update waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(ctx, waitResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network updated")
|
||||
}
|
||||
|
||||
// Delete deletes the resource and removes the Terraform state on success.
|
||||
func (r *networkResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
if !r.isExperimental {
|
||||
v1network.Delete(ctx, req, resp, r.client)
|
||||
} else {
|
||||
v2network.Delete(ctx, req, resp, r.alphaClient)
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Delete existing network
|
||||
err := r.client.DeleteNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = wait.DeleteNetworkWaitHandler(ctx, r.client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Network deletion waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Network deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,network_id
|
||||
func (r *networkResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing network",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[network_id] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
if !r.isExperimental {
|
||||
v1network.ImportState(ctx, req, resp)
|
||||
} else {
|
||||
v2network.ImportState(ctx, req, resp)
|
||||
}
|
||||
|
||||
projectId := idParts[0]
|
||||
networkId := idParts[1]
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("network_id"), networkId)...)
|
||||
tflog.Info(ctx, "Network state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, networkResp *iaas.Network, model *Model) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.NetworkId != nil {
|
||||
networkId = *networkResp.NetworkId
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
if networkResp.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
if networkResp.Gateway != nil {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.GetGateway())
|
||||
} else {
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.NameserversV6 == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.NameserversV6
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.PrefixesV6 == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.PrefixesV6
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
if networkResp.Gatewayv6 != nil {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.GetGatewayv6())
|
||||
} else {
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.PublicIP = types.StringPointerValue(networkResp.PublicIp)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
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")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Prefix.IsNull() || model.IPv6PrefixLength.IsNull() || model.IPv6Nameservers.IsNull()) {
|
||||
addressFamily.Ipv6 = &iaas.CreateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !model.IPv4Prefix.IsNull() || !model.IPv4PrefixLength.IsNull() || !model.IPv4Nameservers.IsNull() || !model.Nameservers.IsNull() {
|
||||
addressFamily.Ipv4 = &iaas.CreateNetworkIPv4Body{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv4Prefix),
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv4PrefixLength),
|
||||
}
|
||||
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
labels, err := conversion.ToStringInterfaceMap(ctx, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaas.CreateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
Routed: conversion.BoolValueToPointer(model.Routed),
|
||||
}
|
||||
|
||||
if addressFamily.Ipv6 != nil || addressFamily.Ipv4 != nil {
|
||||
payload.AddressFamily = addressFamily
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(ctx context.Context, model, stateModel *Model) (*iaas.PartialUpdateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil 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")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
||||
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !model.IPv4Nameservers.IsNull() || !model.Nameservers.IsNull() {
|
||||
addressFamily.Ipv4 = &iaas.UpdateNetworkIPv4Body{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
currentLabels := stateModel.Labels
|
||||
labels, err := conversion.ToJSONMapPartialUpdatePayload(ctx, currentLabels, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaas.PartialUpdateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
}
|
||||
|
||||
if addressFamily.Ipv6 != nil || addressFamily.Ipv4 != nil {
|
||||
payload.AddressFamily = addressFamily
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
|
|
|||
53
stackit/internal/services/iaas/network/utils/model/model.go
Normal file
53
stackit/internal/services/iaas/network/utils/model/model.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package model
|
||||
|
||||
import "github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
NetworkId types.String `tfsdk:"network_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Nameservers types.List `tfsdk:"nameservers"`
|
||||
IPv4Gateway types.String `tfsdk:"ipv4_gateway"`
|
||||
IPv4Nameservers types.List `tfsdk:"ipv4_nameservers"`
|
||||
IPv4Prefix types.String `tfsdk:"ipv4_prefix"`
|
||||
IPv4PrefixLength types.Int64 `tfsdk:"ipv4_prefix_length"`
|
||||
Prefixes types.List `tfsdk:"prefixes"`
|
||||
IPv4Prefixes types.List `tfsdk:"ipv4_prefixes"`
|
||||
IPv6Gateway types.String `tfsdk:"ipv6_gateway"`
|
||||
IPv6Nameservers types.List `tfsdk:"ipv6_nameservers"`
|
||||
IPv6Prefix types.String `tfsdk:"ipv6_prefix"`
|
||||
IPv6PrefixLength types.Int64 `tfsdk:"ipv6_prefix_length"`
|
||||
IPv6Prefixes types.List `tfsdk:"ipv6_prefixes"`
|
||||
PublicIP types.String `tfsdk:"public_ip"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
Routed types.Bool `tfsdk:"routed"`
|
||||
NoIPv4Gateway types.Bool `tfsdk:"no_ipv4_gateway"`
|
||||
NoIPv6Gateway types.Bool `tfsdk:"no_ipv6_gateway"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
RoutingTableID types.String `tfsdk:"routing_table_id"`
|
||||
}
|
||||
|
||||
type DataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
NetworkId types.String `tfsdk:"network_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Nameservers types.List `tfsdk:"nameservers"`
|
||||
IPv4Gateway types.String `tfsdk:"ipv4_gateway"`
|
||||
IPv4Nameservers types.List `tfsdk:"ipv4_nameservers"`
|
||||
IPv4Prefix types.String `tfsdk:"ipv4_prefix"`
|
||||
IPv4PrefixLength types.Int64 `tfsdk:"ipv4_prefix_length"`
|
||||
Prefixes types.List `tfsdk:"prefixes"`
|
||||
IPv4Prefixes types.List `tfsdk:"ipv4_prefixes"`
|
||||
IPv6Gateway types.String `tfsdk:"ipv6_gateway"`
|
||||
IPv6Nameservers types.List `tfsdk:"ipv6_nameservers"`
|
||||
IPv6Prefix types.String `tfsdk:"ipv6_prefix"`
|
||||
IPv6PrefixLength types.Int64 `tfsdk:"ipv6_prefix_length"`
|
||||
IPv6Prefixes types.List `tfsdk:"ipv6_prefixes"`
|
||||
PublicIP types.String `tfsdk:"public_ip"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
Routed types.Bool `tfsdk:"routed"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
RoutingTableID types.String `tfsdk:"routing_table_id"`
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
package v1network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func DatasourceRead(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse, client *iaas.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
var model networkModel.DataSourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
networkResp, err := client.GetNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading network",
|
||||
fmt.Sprintf("Network with ID %q does not exist in project %q.", networkId, projectId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
err = mapDataSourceFields(ctx, networkResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(ctx context.Context, networkResp *iaas.Network, model *networkModel.DataSourceModel) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.NetworkId != nil {
|
||||
networkId = *networkResp.NetworkId
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
|
||||
if networkResp.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
if networkResp.Gateway != nil {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.GetGateway())
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.NameserversV6 == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.NameserversV6
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.PrefixesV6 == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.PrefixesV6
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
if networkResp.Gatewayv6 != nil {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.GetGatewayv6())
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.PublicIP = types.StringPointerValue(networkResp.PublicIp)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
model.RoutingTableID = types.StringNull()
|
||||
model.Region = types.StringNull()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package network
|
||||
package v1network
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -9,19 +9,20 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
)
|
||||
|
||||
func TestMapDataSourceFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state DataSourceModel
|
||||
state networkModel.DataSourceModel
|
||||
input *iaas.Network
|
||||
expected DataSourceModel
|
||||
expected networkModel.DataSourceModel
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"id_ok",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
|
|
@ -29,7 +30,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
NetworkId: utils.Ptr("nid"),
|
||||
Gateway: iaas.NewNullableString(nil),
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -54,7 +55,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"values_ok",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
|
|
@ -85,7 +86,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -130,7 +131,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_nameservers_changed_outside_tf",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -149,7 +150,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
"ns3",
|
||||
},
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -172,7 +173,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_nameservers_changed_outside_tf",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -187,7 +188,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
"ns3",
|
||||
},
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -207,7 +208,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_prefixes_changed_outside_tf",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -222,7 +223,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
"10.100.10.0/16",
|
||||
},
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -248,7 +249,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_prefixes_changed_outside_tf",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -263,7 +264,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
"fd12:3456:789a:4::/64",
|
||||
},
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -286,14 +287,14 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_ipv6_gateway_nil",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaas.Network{
|
||||
NetworkId: utils.Ptr("nid"),
|
||||
},
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -316,18 +317,18 @@ func TestMapDataSourceFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
DataSourceModel{},
|
||||
networkModel.DataSourceModel{},
|
||||
nil,
|
||||
DataSourceModel{},
|
||||
networkModel.DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
DataSourceModel{
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&iaas.Network{},
|
||||
DataSourceModel{},
|
||||
networkModel.DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
package v1network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, client *iaas.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model networkModel.Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Create new network
|
||||
|
||||
network, err := client.CreateNetwork(ctx, projectId).CreateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
networkId := *network.NetworkId
|
||||
network, err = wait.CreateNetworkWaitHandler(ctx, client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Network creation waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, network, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network created")
|
||||
}
|
||||
|
||||
func Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, client *iaas.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
var model networkModel.Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
networkResp, err := client.GetNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, networkResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
func Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, client *iaas.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model networkModel.Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Retrieve values from state
|
||||
var stateModel networkModel.Model
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(ctx, &model, &stateModel)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing network
|
||||
err = client.PartialUpdateNetwork(ctx, projectId, networkId).PartialUpdateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.UpdateNetworkWaitHandler(ctx, client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Network update waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(ctx, waitResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network updated")
|
||||
}
|
||||
|
||||
func Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, client *iaas.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model networkModel.Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Delete existing network
|
||||
err := client.DeleteNetwork(ctx, projectId, networkId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = wait.DeleteNetworkWaitHandler(ctx, client, projectId, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Network deletion waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Network deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,network_id
|
||||
func ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing network",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[network_id] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
projectId := idParts[0]
|
||||
networkId := idParts[1]
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("network_id"), networkId)...)
|
||||
tflog.Info(ctx, "Network state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, networkResp *iaas.Network, model *networkModel.Model) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.NetworkId != nil {
|
||||
networkId = *networkResp.NetworkId
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
if networkResp.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
if networkResp.Gateway != nil {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.GetGateway())
|
||||
} else {
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.NameserversV6 == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.NameserversV6
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.PrefixesV6 == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.PrefixesV6
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
if networkResp.Gatewayv6 != nil {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.GetGatewayv6())
|
||||
} else {
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.PublicIP = types.StringPointerValue(networkResp.PublicIp)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
model.Region = types.StringNull()
|
||||
model.RoutingTableID = types.StringNull()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaas.CreateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
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")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Prefix.IsNull() || model.IPv6PrefixLength.IsNull() || model.IPv6Nameservers.IsNull()) {
|
||||
addressFamily.Ipv6 = &iaas.CreateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv6PrefixLength),
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !model.IPv4Prefix.IsNull() || !model.IPv4PrefixLength.IsNull() || !model.IPv4Nameservers.IsNull() || !model.Nameservers.IsNull() {
|
||||
addressFamily.Ipv4 = &iaas.CreateNetworkIPv4Body{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv4Prefix),
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv4PrefixLength),
|
||||
}
|
||||
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
labels, err := conversion.ToStringInterfaceMap(ctx, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaas.CreateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
Routed: conversion.BoolValueToPointer(model.Routed),
|
||||
}
|
||||
|
||||
if addressFamily.Ipv6 != nil || addressFamily.Ipv4 != nil {
|
||||
payload.AddressFamily = addressFamily
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model) (*iaas.PartialUpdateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil 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")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
||||
addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
addressFamily.Ipv6.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
if !model.IPv4Nameservers.IsNull() || !model.Nameservers.IsNull() {
|
||||
addressFamily.Ipv4 = &iaas.UpdateNetworkIPv4Body{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
addressFamily.Ipv4.Gateway = iaas.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
}
|
||||
currentLabels := stateModel.Labels
|
||||
labels, err := conversion.ToJSONMapPartialUpdatePayload(ctx, currentLabels, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaas.PartialUpdateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
}
|
||||
|
||||
if addressFamily.Ipv6 != nil || addressFamily.Ipv4 != nil {
|
||||
payload.AddressFamily = addressFamily
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package network
|
||||
package v1network
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -9,19 +9,20 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state Model
|
||||
state model.Model
|
||||
input *iaas.Network
|
||||
expected Model
|
||||
expected model.Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"id_ok",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
|
|
@ -29,7 +30,7 @@ func TestMapFields(t *testing.T) {
|
|||
NetworkId: utils.Ptr("nid"),
|
||||
Gateway: iaas.NewNullableString(nil),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -54,7 +55,7 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"values_ok",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
|
|
@ -85,7 +86,7 @@ func TestMapFields(t *testing.T) {
|
|||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -130,7 +131,7 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_nameservers_changed_outside_tf",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -149,7 +150,7 @@ func TestMapFields(t *testing.T) {
|
|||
"ns3",
|
||||
},
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -172,7 +173,7 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_nameservers_changed_outside_tf",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -187,7 +188,7 @@ func TestMapFields(t *testing.T) {
|
|||
"ns3",
|
||||
},
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -207,7 +208,7 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_prefixes_changed_outside_tf",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -222,7 +223,7 @@ func TestMapFields(t *testing.T) {
|
|||
"192.168.55.0/24",
|
||||
},
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -248,7 +249,7 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_prefixes_changed_outside_tf",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
|
|
@ -263,7 +264,7 @@ func TestMapFields(t *testing.T) {
|
|||
"fd12:3456:789a:2::/64",
|
||||
},
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -286,14 +287,14 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_ipv6_gateway_nil",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaas.Network{
|
||||
NetworkId: utils.Ptr("nid"),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
|
|
@ -316,18 +317,18 @@ func TestMapFields(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
Model{},
|
||||
model.Model{},
|
||||
nil,
|
||||
Model{},
|
||||
model.Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&iaas.Network{},
|
||||
Model{},
|
||||
model.Model{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
|
@ -353,13 +354,13 @@ func TestMapFields(t *testing.T) {
|
|||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
input *model.Model
|
||||
expected *iaas.CreateNetworkPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -395,7 +396,7 @@ func TestToCreatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_nameservers_okay",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -431,7 +432,7 @@ func TestToCreatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_default_ok",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -488,14 +489,14 @@ func TestToCreatePayload(t *testing.T) {
|
|||
func TestToUpdatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
state Model
|
||||
input *model.Model
|
||||
state model.Model
|
||||
expected *iaas.PartialUpdateNetworkPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -507,7 +508,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
|
|
@ -531,7 +532,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_nameservers_okay",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -543,7 +544,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
|
|
@ -567,7 +568,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv4_gateway_nil",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -578,7 +579,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
|
|
@ -601,7 +602,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_default_ok",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -613,7 +614,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
Routed: types.BoolValue(true),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
|
|
@ -637,7 +638,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"ipv6_gateway_nil",
|
||||
&Model{
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
|
|
@ -648,7 +649,7 @@ func TestToUpdatePayload(t *testing.T) {
|
|||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
},
|
||||
Model{
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
package v2network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func DatasourceRead(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse, client *iaasalpha.APIClient, providerData core.ProviderData) { // nolint:gocritic // function signature required by Terraform
|
||||
var model networkModel.DataSourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
region := providerData.GetRegionWithOverride(model.Region)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
networkResp, err := client.GetNetwork(ctx, projectId, region, networkId).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading network",
|
||||
fmt.Sprintf("Network with ID %q does not exist in project %q.", networkId, projectId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
err = mapDataSourceFields(ctx, networkResp, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(ctx context.Context, networkResp *iaasalpha.Network, model *networkModel.DataSourceModel, region string) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.Id != nil {
|
||||
networkId = *networkResp.Id
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Ipv4.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Ipv4.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Gateway == nil {
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
} else {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.Ipv4.GetGateway())
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.PublicIp == nil {
|
||||
model.PublicIP = types.StringNull()
|
||||
} else {
|
||||
model.PublicIP = types.StringPointerValue(networkResp.Ipv4.PublicIp)
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Nameservers == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.Ipv6.Nameservers
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Prefixes == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.Ipv6.Prefixes
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Gateway == nil {
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
} else {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.Ipv6.GetGateway())
|
||||
}
|
||||
|
||||
model.RoutingTableID = types.StringNull()
|
||||
if networkResp.RoutingTableId != nil {
|
||||
model.RoutingTableID = types.StringValue(*networkResp.RoutingTableId)
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
model.Region = types.StringValue(region)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
package v2network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
)
|
||||
|
||||
const (
|
||||
testRegion = "region"
|
||||
)
|
||||
|
||||
func TestMapDataSourceFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state networkModel.DataSourceModel
|
||||
input *iaasalpha.Network
|
||||
region string
|
||||
expected networkModel.DataSourceModel
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"id_ok",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Gateway: iaasalpha.NewNullableString(nil),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
IPv4Gateway: types.StringNull(),
|
||||
IPv4Prefix: types.StringNull(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Gateway: types.StringNull(),
|
||||
IPv6Prefix: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
PublicIP: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Routed: types.BoolNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"values_ok",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Nameservers: &[]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
},
|
||||
Prefixes: &[]string{
|
||||
"192.168.42.0/24",
|
||||
"10.100.10.0/16",
|
||||
},
|
||||
PublicIp: utils.Ptr("publicIp"),
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Nameservers: &[]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
},
|
||||
Prefixes: &[]string{
|
||||
"fd12:3456:789a:1::/64",
|
||||
"fd12:3456:789a:2::/64",
|
||||
},
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(true),
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4PrefixLength: types.Int64Value(24),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
IPv4Prefix: types.StringValue("192.168.42.0/24"),
|
||||
IPv4Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv6PrefixLength: types.Int64Value(64),
|
||||
IPv6Prefix: types.StringValue("fd12:3456:789a:1::/64"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:1::/64"),
|
||||
types.StringValue("fd12:3456:789a:2::/64"),
|
||||
}),
|
||||
PublicIP: types.StringValue("publicIp"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_nameservers_changed_outside_tf",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Nameservers: &[]string{
|
||||
"ns2",
|
||||
"ns3",
|
||||
},
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameservers_changed_outside_tf",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Nameservers: &[]string{
|
||||
"ns2",
|
||||
"ns3",
|
||||
},
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_prefixes_changed_outside_tf",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Prefixes: &[]string{
|
||||
"10.100.20.0/16",
|
||||
"10.100.10.0/16",
|
||||
},
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Value(16),
|
||||
IPv4Prefix: types.StringValue("10.100.20.0/16"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("10.100.20.0/16"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
IPv4Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("10.100.20.0/16"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_prefixes_changed_outside_tf",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:1::/64"),
|
||||
types.StringValue("fd12:3456:789a:2::/64"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Prefixes: &[]string{
|
||||
"fd12:3456:789a:3::/64",
|
||||
"fd12:3456:789a:4::/64",
|
||||
},
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Value(64),
|
||||
IPv6Prefix: types.StringValue("fd12:3456:789a:3::/64"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:3::/64"),
|
||||
types.StringValue("fd12:3456:789a:4::/64"),
|
||||
}),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_ipv6_gateway_nil",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
IPv4Gateway: types.StringNull(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Gateway: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
PublicIP: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Routed: types.BoolNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
networkModel.DataSourceModel{},
|
||||
nil,
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
networkModel.DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&iaasalpha.Network{},
|
||||
testRegion,
|
||||
networkModel.DataSourceModel{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapDataSourceFields(context.Background(), tt.input, &tt.state, 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(tt.state, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,555 @@
|
|||
package v2network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
networkModel "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, client *iaasalpha.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model networkModel.Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(ctx, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Create new network
|
||||
|
||||
network, err := client.CreateNetwork(ctx, projectId, region).CreateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
networkId := *network.Id
|
||||
network, err = wait.CreateNetworkWaitHandler(ctx, client, projectId, region, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Network creation waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, network, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network created")
|
||||
}
|
||||
|
||||
func Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, client *iaasalpha.APIClient, providerData core.ProviderData) { // nolint:gocritic // function signature required by Terraform
|
||||
var model networkModel.Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
region := providerData.GetRegionWithOverride(model.Region)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
networkResp, err := client.GetNetwork(ctx, projectId, region, networkId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, networkResp, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network read")
|
||||
}
|
||||
|
||||
func Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, client *iaasalpha.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model networkModel.Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Retrieve values from state
|
||||
var stateModel networkModel.Model
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(ctx, &model, &stateModel)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing network
|
||||
err = client.PartialUpdateNetwork(ctx, projectId, region, networkId).PartialUpdateNetworkPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.UpdateNetworkWaitHandler(ctx, client, projectId, region, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Network update waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(ctx, waitResp, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating network", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Network updated")
|
||||
}
|
||||
|
||||
func Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, client *iaasalpha.APIClient) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model networkModel.Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
networkId := model.NetworkId.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Delete existing network
|
||||
err := client.DeleteNetwork(ctx, projectId, region, networkId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = wait.DeleteNetworkWaitHandler(ctx, client, projectId, region, networkId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Network deletion waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Network deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,region,network_id
|
||||
func ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing network",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[network_id] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
projectId := idParts[0]
|
||||
region := idParts[1]
|
||||
networkId := idParts[2]
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "network_id", networkId)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), region)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("network_id"), networkId)...)
|
||||
tflog.Info(ctx, "Network state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, networkResp *iaasalpha.Network, model *networkModel.Model, region string) error {
|
||||
if networkResp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var networkId string
|
||||
if model.NetworkId.ValueString() != "" {
|
||||
networkId = model.NetworkId.ValueString()
|
||||
} else if networkResp.Id != nil {
|
||||
networkId = *networkResp.Id
|
||||
} else {
|
||||
return fmt.Errorf("network id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, networkId)
|
||||
|
||||
labels, err := iaasUtils.MapLabels(ctx, networkResp.Labels, model.Labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IPv4
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Nameservers == nil {
|
||||
model.Nameservers = types.ListNull(types.StringType)
|
||||
model.IPv4Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respNameservers := *networkResp.Ipv4.Nameservers
|
||||
modelNameservers, err := utils.ListValuetoStringSlice(model.Nameservers)
|
||||
modelIPv4Nameservers, errIpv4 := utils.ListValuetoStringSlice(model.IPv4Nameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get current network nameservers from model: %w", err)
|
||||
}
|
||||
if errIpv4 != nil {
|
||||
return fmt.Errorf("get current IPv4 network nameservers from model: %w", errIpv4)
|
||||
}
|
||||
|
||||
reconciledNameservers := utils.ReconcileStringSlices(modelNameservers, respNameservers)
|
||||
reconciledIPv4Nameservers := utils.ReconcileStringSlices(modelIPv4Nameservers, respNameservers)
|
||||
|
||||
nameserversTF, diags := types.ListValueFrom(ctx, types.StringType, reconciledNameservers)
|
||||
ipv4NameserversTF, ipv4Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv4Nameservers)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network nameservers: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if ipv4Diags.HasError() {
|
||||
return fmt.Errorf("map IPv4 network nameservers: %w", core.DiagsToError(ipv4Diags))
|
||||
}
|
||||
|
||||
model.Nameservers = nameserversTF
|
||||
model.IPv4Nameservers = ipv4NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Prefixes == nil {
|
||||
model.Prefixes = types.ListNull(types.StringType)
|
||||
model.IPv4Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixes := *networkResp.Ipv4.Prefixes
|
||||
prefixesTF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixes)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixes) > 0 {
|
||||
model.IPv4Prefix = types.StringValue(respPrefixes[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixes[0])
|
||||
if err != nil {
|
||||
tflog.Error(ctx, fmt.Sprintf("ipv4_prefix_length: %+v", err))
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv4PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv4PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
|
||||
model.Prefixes = prefixesTF
|
||||
model.IPv4Prefixes = prefixesTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.Gateway == nil {
|
||||
model.IPv4Gateway = types.StringNull()
|
||||
} else {
|
||||
model.IPv4Gateway = types.StringPointerValue(networkResp.Ipv4.GetGateway())
|
||||
}
|
||||
|
||||
if networkResp.Ipv4 == nil || networkResp.Ipv4.PublicIp == nil {
|
||||
model.PublicIP = types.StringNull()
|
||||
} else {
|
||||
model.PublicIP = types.StringPointerValue(networkResp.Ipv4.PublicIp)
|
||||
}
|
||||
|
||||
// IPv6
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Nameservers == nil {
|
||||
model.IPv6Nameservers = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respIPv6Nameservers := *networkResp.Ipv6.Nameservers
|
||||
modelIPv6Nameservers, errIpv6 := utils.ListValuetoStringSlice(model.IPv6Nameservers)
|
||||
if errIpv6 != nil {
|
||||
return fmt.Errorf("get current IPv6 network nameservers from model: %w", errIpv6)
|
||||
}
|
||||
|
||||
reconciledIPv6Nameservers := utils.ReconcileStringSlices(modelIPv6Nameservers, respIPv6Nameservers)
|
||||
|
||||
ipv6NameserversTF, ipv6Diags := types.ListValueFrom(ctx, types.StringType, reconciledIPv6Nameservers)
|
||||
if ipv6Diags.HasError() {
|
||||
return fmt.Errorf("map IPv6 network nameservers: %w", core.DiagsToError(ipv6Diags))
|
||||
}
|
||||
|
||||
model.IPv6Nameservers = ipv6NameserversTF
|
||||
}
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Prefixes == nil {
|
||||
model.IPv6Prefixes = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respPrefixesV6 := *networkResp.Ipv6.Prefixes
|
||||
prefixesV6TF, diags := types.ListValueFrom(ctx, types.StringType, respPrefixesV6)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("map network IPv6 prefixes: %w", core.DiagsToError(diags))
|
||||
}
|
||||
if len(respPrefixesV6) > 0 {
|
||||
model.IPv6Prefix = types.StringValue(respPrefixesV6[0])
|
||||
_, netmask, err := net.ParseCIDR(respPrefixesV6[0])
|
||||
if err != nil {
|
||||
// silently ignore parsing error for the netmask
|
||||
model.IPv6PrefixLength = types.Int64Null()
|
||||
} else {
|
||||
ones, _ := netmask.Mask.Size()
|
||||
model.IPv6PrefixLength = types.Int64Value(int64(ones))
|
||||
}
|
||||
}
|
||||
model.IPv6Prefixes = prefixesV6TF
|
||||
}
|
||||
|
||||
if networkResp.Ipv6 == nil || networkResp.Ipv6.Gateway == nil {
|
||||
model.IPv6Gateway = types.StringNull()
|
||||
} else {
|
||||
model.IPv6Gateway = types.StringPointerValue(networkResp.Ipv6.GetGateway())
|
||||
}
|
||||
|
||||
if networkResp.RoutingTableId != nil {
|
||||
model.RoutingTableID = types.StringPointerValue(networkResp.RoutingTableId)
|
||||
} else {
|
||||
model.RoutingTableID = types.StringNull()
|
||||
}
|
||||
|
||||
model.NetworkId = types.StringValue(networkId)
|
||||
model.Name = types.StringPointerValue(networkResp.Name)
|
||||
model.Labels = labels
|
||||
model.Routed = types.BoolPointerValue(networkResp.Routed)
|
||||
model.Region = types.StringValue(region)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(ctx context.Context, model *networkModel.Model) (*iaasalpha.CreateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
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")
|
||||
}
|
||||
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),
|
||||
},
|
||||
}
|
||||
} else if !utils.IsUndefined(model.IPv6Prefix) {
|
||||
var gateway *iaasalpha.NullableString
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
gateway = iaasalpha.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
gateway = iaasalpha.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
|
||||
ipv6Body = &iaasalpha.CreateNetworkIPv6{
|
||||
CreateNetworkIPv6WithPrefix: &iaasalpha.CreateNetworkIPv6WithPrefix{
|
||||
Gateway: gateway,
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv6Prefix),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
var ipv4Body *iaasalpha.CreateNetworkIPv4
|
||||
if !utils.IsUndefined(model.IPv4PrefixLength) {
|
||||
ipv4Body = &iaasalpha.CreateNetworkIPv4{
|
||||
CreateNetworkIPv4WithPrefixLength: &iaasalpha.CreateNetworkIPv4WithPrefixLength{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
PrefixLength: conversion.Int64ValueToPointer(model.IPv4PrefixLength),
|
||||
},
|
||||
}
|
||||
} else if !utils.IsUndefined(model.IPv4Prefix) {
|
||||
var gateway *iaasalpha.NullableString
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
gateway = iaasalpha.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
gateway = iaasalpha.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
|
||||
ipv4Body = &iaasalpha.CreateNetworkIPv4{
|
||||
CreateNetworkIPv4WithPrefix: &iaasalpha.CreateNetworkIPv4WithPrefix{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
Prefix: conversion.StringValueToPointer(model.IPv4Prefix),
|
||||
Gateway: gateway,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
labels, err := conversion.ToStringInterfaceMap(ctx, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaasalpha.CreateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
Routed: conversion.BoolValueToPointer(model.Routed),
|
||||
Ipv4: ipv4Body,
|
||||
Ipv6: ipv6Body,
|
||||
RoutingTableId: conversion.StringValueToPointer(model.RoutingTableID),
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(ctx context.Context, model, stateModel *networkModel.Model) (*iaasalpha.PartialUpdateNetworkPayload, error) {
|
||||
if model == nil {
|
||||
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")
|
||||
}
|
||||
modelIPv6Nameservers = append(modelIPv6Nameservers, ipv6NameserverString.ValueString())
|
||||
}
|
||||
|
||||
var ipv6Body *iaasalpha.UpdateNetworkIPv6Body
|
||||
if !(model.IPv6Nameservers.IsNull() || model.IPv6Nameservers.IsUnknown()) {
|
||||
ipv6Body = &iaasalpha.UpdateNetworkIPv6Body{
|
||||
Nameservers: &modelIPv6Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv6Gateway.ValueBool() {
|
||||
ipv6Body.Gateway = iaasalpha.NewNullableString(nil)
|
||||
} else if !(model.IPv6Gateway.IsUnknown() || model.IPv6Gateway.IsNull()) {
|
||||
ipv6Body.Gateway = iaasalpha.NewNullableString(conversion.StringValueToPointer(model.IPv6Gateway))
|
||||
}
|
||||
}
|
||||
|
||||
modelIPv4Nameservers := []string{}
|
||||
var modelIPv4List []attr.Value
|
||||
|
||||
if !(model.IPv4Nameservers.IsNull() || model.IPv4Nameservers.IsUnknown()) {
|
||||
modelIPv4List = model.IPv4Nameservers.Elements()
|
||||
} else {
|
||||
modelIPv4List = model.Nameservers.Elements()
|
||||
}
|
||||
for _, ipv4ns := range modelIPv4List {
|
||||
ipv4NameserverString, ok := ipv4ns.(types.String)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
modelIPv4Nameservers = append(modelIPv4Nameservers, ipv4NameserverString.ValueString())
|
||||
}
|
||||
|
||||
var ipv4Body *iaasalpha.UpdateNetworkIPv4Body
|
||||
if !model.IPv4Nameservers.IsNull() || !model.Nameservers.IsNull() {
|
||||
ipv4Body = &iaasalpha.UpdateNetworkIPv4Body{
|
||||
Nameservers: &modelIPv4Nameservers,
|
||||
}
|
||||
|
||||
if model.NoIPv4Gateway.ValueBool() {
|
||||
ipv4Body.Gateway = iaasalpha.NewNullableString(nil)
|
||||
} else if !(model.IPv4Gateway.IsUnknown() || model.IPv4Gateway.IsNull()) {
|
||||
ipv4Body.Gateway = iaasalpha.NewNullableString(conversion.StringValueToPointer(model.IPv4Gateway))
|
||||
}
|
||||
}
|
||||
currentLabels := stateModel.Labels
|
||||
labels, err := conversion.ToJSONMapPartialUpdatePayload(ctx, currentLabels, model.Labels)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to Go map: %w", err)
|
||||
}
|
||||
|
||||
payload := iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Labels: &labels,
|
||||
Ipv4: ipv4Body,
|
||||
Ipv6: ipv6Body,
|
||||
RoutingTableId: conversion.StringValueToPointer(model.RoutingTableID),
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,707 @@
|
|||
package v2network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network/utils/model"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
const testRegion = "region"
|
||||
tests := []struct {
|
||||
description string
|
||||
state model.Model
|
||||
input *iaasalpha.Network
|
||||
region string
|
||||
expected model.Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"id_ok",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Gateway: iaasalpha.NewNullableString(nil),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
IPv4Gateway: types.StringNull(),
|
||||
IPv4Prefix: types.StringNull(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Gateway: types.StringNull(),
|
||||
IPv6Prefix: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
PublicIP: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Routed: types.BoolNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"values_ok",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Nameservers: utils.Ptr([]string{"ns1", "ns2"}),
|
||||
Prefixes: utils.Ptr(
|
||||
[]string{
|
||||
"192.168.42.0/24",
|
||||
"10.100.10.0/16",
|
||||
},
|
||||
),
|
||||
PublicIp: utils.Ptr("publicIp"),
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Nameservers: utils.Ptr([]string{"ns1", "ns2"}),
|
||||
Prefixes: utils.Ptr([]string{
|
||||
"fd12:3456:789a:1::/64",
|
||||
"fd12:3456:789b:1::/64",
|
||||
}),
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(true),
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4PrefixLength: types.Int64Value(24),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
IPv4Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/16"),
|
||||
}),
|
||||
IPv4Prefix: types.StringValue("192.168.42.0/24"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv6PrefixLength: types.Int64Value(64),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:1::/64"),
|
||||
types.StringValue("fd12:3456:789b:1::/64"),
|
||||
}),
|
||||
IPv6Prefix: types.StringValue("fd12:3456:789a:1::/64"),
|
||||
PublicIP: types.StringValue("publicIp"),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
IPv6Gateway: types.StringValue("gateway"),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_nameservers_changed_outside_tf",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns2",
|
||||
"ns3",
|
||||
}),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_nameservers_changed_outside_tf",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns2",
|
||||
"ns3",
|
||||
}),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns2"),
|
||||
types.StringValue("ns3"),
|
||||
}),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_prefixes_changed_outside_tf",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.42.0/24"),
|
||||
types.StringValue("10.100.10.0/24"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv4: &iaasalpha.NetworkIPv4{
|
||||
Prefixes: utils.Ptr(
|
||||
[]string{
|
||||
"192.168.54.0/24",
|
||||
"192.168.55.0/24",
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Value(24),
|
||||
IPv4Prefix: types.StringValue("192.168.54.0/24"),
|
||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.54.0/24"),
|
||||
types.StringValue("192.168.55.0/24"),
|
||||
}),
|
||||
IPv4Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("192.168.54.0/24"),
|
||||
types.StringValue("192.168.55.0/24"),
|
||||
}),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_prefixes_changed_outside_tf",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:1::/64"),
|
||||
types.StringValue("fd12:3456:789a:2::/64"),
|
||||
}),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
Ipv6: &iaasalpha.NetworkIPv6{
|
||||
Prefixes: utils.Ptr(
|
||||
[]string{
|
||||
"fd12:3456:789a:1::/64",
|
||||
"fd12:3456:789a:2::/64",
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Value(64),
|
||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("fd12:3456:789a:1::/64"),
|
||||
types.StringValue("fd12:3456:789a:2::/64"),
|
||||
}),
|
||||
IPv6Prefix: types.StringValue("fd12:3456:789a:1::/64"),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_ipv6_gateway_nil",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
},
|
||||
&iaasalpha.Network{
|
||||
Id: utils.Ptr("nid"),
|
||||
},
|
||||
testRegion,
|
||||
model.Model{
|
||||
Id: types.StringValue("pid,region,nid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Name: types.StringNull(),
|
||||
Nameservers: types.ListNull(types.StringType),
|
||||
IPv4Nameservers: types.ListNull(types.StringType),
|
||||
IPv4PrefixLength: types.Int64Null(),
|
||||
IPv4Gateway: types.StringNull(),
|
||||
Prefixes: types.ListNull(types.StringType),
|
||||
IPv4Prefixes: types.ListNull(types.StringType),
|
||||
IPv6Nameservers: types.ListNull(types.StringType),
|
||||
IPv6PrefixLength: types.Int64Null(),
|
||||
IPv6Gateway: types.StringNull(),
|
||||
IPv6Prefixes: types.ListNull(types.StringType),
|
||||
PublicIP: types.StringNull(),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
Routed: types.BoolNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response_nil_fail",
|
||||
model.Model{},
|
||||
nil,
|
||||
testRegion,
|
||||
model.Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&iaasalpha.Network{},
|
||||
testRegion,
|
||||
model.Model{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapFields(context.Background(), tt.input, &tt.state, 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(tt.state, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *model.Model
|
||||
expected *iaasalpha.CreateNetworkPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
IPv4Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaasalpha.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.CreateNetworkIPv4{
|
||||
CreateNetworkIPv4WithPrefix: &iaasalpha.CreateNetworkIPv4WithPrefix{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_nameservers_okay",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(false),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
IPv4Prefix: types.StringValue("prefix"),
|
||||
},
|
||||
&iaasalpha.CreateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.CreateNetworkIPv4{
|
||||
CreateNetworkIPv4WithPrefix: &iaasalpha.CreateNetworkIPv4WithPrefix{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
Prefix: utils.Ptr("prefix"),
|
||||
},
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
Routed: utils.Ptr(false),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_default_ok",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
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{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
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) {
|
||||
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, cmp.AllowUnexported(iaasalpha.NullableString{}))
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToUpdatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *model.Model
|
||||
state model.Model
|
||||
expected *iaasalpha.PartialUpdateNetworkPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_ok",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.UpdateNetworkIPv4Body{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_nameservers_okay",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
IPv4Gateway: types.StringValue("gateway"),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.UpdateNetworkIPv4Body{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv4_gateway_nil",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
},
|
||||
model.Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
NetworkId: types.StringValue("nid"),
|
||||
Labels: types.MapNull(types.StringType),
|
||||
},
|
||||
&iaasalpha.PartialUpdateNetworkPayload{
|
||||
Name: utils.Ptr("name"),
|
||||
Ipv4: &iaasalpha.UpdateNetworkIPv4Body{
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_default_ok",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
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{
|
||||
Gateway: iaasalpha.NewNullableString(utils.Ptr("gateway")),
|
||||
Nameservers: utils.Ptr([]string{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ipv6_gateway_nil",
|
||||
&model.Model{
|
||||
Name: types.StringValue("name"),
|
||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ns1"),
|
||||
types.StringValue("ns2"),
|
||||
}),
|
||||
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"key": types.StringValue("value"),
|
||||
}),
|
||||
Routed: types.BoolValue(true),
|
||||
},
|
||||
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{
|
||||
"ns1",
|
||||
"ns2",
|
||||
}),
|
||||
},
|
||||
Labels: &map[string]interface{}{
|
||||
"key": "value",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toUpdatePayload(context.Background(), tt.input, &tt.state)
|
||||
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, cmp.AllowUnexported(iaasalpha.NullableString{}))
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,32 @@
|
|||
variable "project_id" {}
|
||||
variable "name" {}
|
||||
variable "ipv4_gateway" {}
|
||||
variable "ipv4_nameservers" {}
|
||||
variable "ipv4_nameserver_0" {}
|
||||
variable "ipv4_nameserver_1" {}
|
||||
variable "ipv4_prefix" {}
|
||||
variable "ipv4_prefix_length" {}
|
||||
variable "routed" {}
|
||||
variable "label" {}
|
||||
|
||||
resource "stackit_network" "network" {
|
||||
resource "stackit_network" "network_prefix" {
|
||||
project_id = var.project_id
|
||||
name = var.name
|
||||
ipv4_gateway = var.ipv4_gateway != "" ? var.ipv4_gateway : null
|
||||
no_ipv4_gateway = var.ipv4_gateway != "" ? null : true
|
||||
ipv4_nameservers = [var.ipv4_nameservers]
|
||||
ipv4_nameservers = [var.ipv4_nameserver_0, var.ipv4_nameserver_1]
|
||||
ipv4_prefix = var.ipv4_prefix
|
||||
routed = var.routed
|
||||
labels = {
|
||||
"acc-test" : var.label
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "stackit_network" "network_prefix_length" {
|
||||
project_id = var.project_id
|
||||
name = var.name
|
||||
no_ipv4_gateway = true
|
||||
ipv4_nameservers = [var.ipv4_nameserver_0, var.ipv4_nameserver_1]
|
||||
ipv4_prefix_length = var.ipv4_prefix_length
|
||||
routed = var.routed
|
||||
labels = {
|
||||
43
stackit/internal/services/iaas/testdata/resource-network-v2-max.tf
vendored
Normal file
43
stackit/internal/services/iaas/testdata/resource-network-v2-max.tf
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
variable "project_id" {}
|
||||
variable "name" {}
|
||||
variable "ipv4_gateway" {}
|
||||
variable "ipv4_nameserver_0" {}
|
||||
variable "ipv4_nameserver_1" {}
|
||||
variable "ipv4_prefix" {}
|
||||
variable "ipv4_prefix_length" {}
|
||||
variable "routed" {}
|
||||
variable "label" {}
|
||||
variable "organization_id" {}
|
||||
variable "network_area_id" {}
|
||||
|
||||
# resource "stackit_network" "network_prefix" {
|
||||
# project_id = var.project_id
|
||||
# name = var.name
|
||||
# # ipv4_gateway = var.ipv4_gateway != "" ? var.ipv4_gateway : null
|
||||
# # no_ipv4_gateway = var.ipv4_gateway != "" ? null : true
|
||||
# ipv4_nameservers = [var.ipv4_nameserver_0, var.ipv4_nameserver_1]
|
||||
# ipv4_prefix = var.ipv4_prefix
|
||||
# routed = var.routed
|
||||
# labels = {
|
||||
# "acc-test" : var.label
|
||||
# }
|
||||
# }
|
||||
|
||||
resource "stackit_network" "network_prefix_length" {
|
||||
project_id = var.project_id
|
||||
name = var.name
|
||||
# no_ipv4_gateway = true
|
||||
ipv4_nameservers = [var.ipv4_nameserver_0, var.ipv4_nameserver_1]
|
||||
ipv4_prefix_length = var.ipv4_prefix_length
|
||||
routed = var.routed
|
||||
labels = {
|
||||
"acc-test" : var.label
|
||||
}
|
||||
routing_table_id = stackit_routing_table.routing_table.routing_table_id
|
||||
}
|
||||
|
||||
resource "stackit_routing_table" "routing_table" {
|
||||
organization_id = var.organization_id
|
||||
network_area_id = var.network_area_id
|
||||
name = var.name
|
||||
}
|
||||
7
stackit/internal/services/iaas/testdata/resource-network-v2-min.tf
vendored
Normal file
7
stackit/internal/services/iaas/testdata/resource-network-v2-min.tf
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
variable "project_id" {}
|
||||
variable "name" {}
|
||||
|
||||
resource "stackit_network" "network" {
|
||||
project_id = var.project_id
|
||||
name = var.name
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -19,8 +20,6 @@ import (
|
|||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
|
||||
"maps"
|
||||
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||
)
|
||||
|
||||
|
|
@ -123,7 +122,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMin["organization_id"])),
|
||||
|
|
@ -158,7 +157,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
network_area_id = stackit_routing_table.routing_table.network_area_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
|
|
@ -232,7 +231,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableMinUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMinUpdated["organization_id"])),
|
||||
|
|
@ -261,7 +260,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMax["organization_id"])),
|
||||
|
|
@ -297,7 +296,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
network_area_id = stackit_routing_table.routing_table.network_area_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
|
|
@ -373,7 +372,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableMaxUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMaxUpdated["organization_id"])),
|
||||
|
|
@ -403,7 +402,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableRouteMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMin["organization_id"])),
|
||||
|
|
@ -451,7 +450,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
routing_table_id = stackit_routing_table_route.route.routing_table_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table route
|
||||
|
|
@ -529,7 +528,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableRouteMinUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMinUpdated["organization_id"])),
|
||||
|
|
@ -569,7 +568,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableRouteMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMax["organization_id"])),
|
||||
|
|
@ -618,7 +617,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
routing_table_id = stackit_routing_table_route.route.routing_table_id
|
||||
}
|
||||
`,
|
||||
testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig,
|
||||
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig,
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table route
|
||||
|
|
@ -698,7 +697,7 @@ func TestAccRoutingTable(t *testing.T) {
|
|||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigRoutingTableRouteMaxUpdated,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Routing table
|
||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMaxUpdated["organization_id"])),
|
||||
|
|
|
|||
|
|
@ -122,13 +122,28 @@ func IaaSProviderConfig() string {
|
|||
return `
|
||||
provider "stackit" {
|
||||
default_region = "eu01"
|
||||
experiments = ["routing-tables"]
|
||||
}`
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
iaas_custom_endpoint = "%s"
|
||||
experiments = ["routing-tables"]
|
||||
}`,
|
||||
IaaSCustomEndpoint,
|
||||
)
|
||||
}
|
||||
|
||||
func IaaSProviderConfigWithExperiments() string {
|
||||
if IaaSCustomEndpoint == "" {
|
||||
return `
|
||||
provider "stackit" {
|
||||
default_region = "eu01"
|
||||
experiments = [ "routing-tables", "network" ]
|
||||
}`
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
iaas_custom_endpoint = "%s"
|
||||
experiments = [ "routing-tables", "network" ]
|
||||
}`,
|
||||
IaaSCustomEndpoint,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package stackit
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
|
|
@ -186,7 +187,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
"service_enablement_custom_endpoint": "Custom endpoint for the Service Enablement API",
|
||||
"token_custom_endpoint": "Custom endpoint for the token API, which is used to request access tokens when using the key flow",
|
||||
"enable_beta_resources": "Enable beta resources. Default is false.",
|
||||
"experiments": fmt.Sprintf("Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: %v", features.AvailableExperiments),
|
||||
"experiments": fmt.Sprintf("Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: %v", strings.Join(features.AvailableExperiments, ", ")),
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue