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
|
|
@ -183,7 +183,7 @@ To enable experiments set the experiments field in the provider definition:
|
||||||
```hcl
|
```hcl
|
||||||
provider "stackit" {
|
provider "stackit" {
|
||||||
default_region = "eu01"
|
default_region = "eu01"
|
||||||
experiments = ["iam", "routing-tables"]
|
experiments = ["iam", "routing-tables", "network"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -197,6 +197,12 @@ Enables IAM management features in the Terraform provider. The underlying IAM AP
|
||||||
|
|
||||||
This feature enables experimental routing table capabilities in the Terraform Provider, available only to designated SNAs at this time.
|
This feature enables experimental routing table capabilities in the Terraform Provider, available only to designated SNAs at this time.
|
||||||
|
|
||||||
|
#### `network`
|
||||||
|
|
||||||
|
The `stackit_network` provides the fields `region` and `routing_table_id` when the experiment flag `network` is set.
|
||||||
|
The underlying API is not stable yet and could change in the future.
|
||||||
|
If you don't need these fields, don't set the experiment flag `network`, to use the stable api.
|
||||||
|
|
||||||
## Acceptance Tests
|
## Acceptance Tests
|
||||||
|
|
||||||
Terraform acceptance tests are run using the command `make test-acceptance-tf`. For all services,
|
Terraform acceptance tests are run using the command `make test-acceptance-tf`. For all services,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,11 @@ data "stackit_network" "example" {
|
||||||
- `network_id` (String) The network ID.
|
- `network_id` (String) The network ID.
|
||||||
- `project_id` (String) STACKIT project ID to which the network is associated.
|
- `project_id` (String) STACKIT project ID to which the network is associated.
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
- `region` (String) Can only be used when experimental "network" is set. This is likely going to undergo significant changes or be removed in the future.
|
||||||
|
The resource region. If not defined, the provider region is used.
|
||||||
|
|
||||||
### Read-Only
|
### Read-Only
|
||||||
|
|
||||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`network_id`".
|
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`network_id`".
|
||||||
|
|
@ -46,3 +51,5 @@ data "stackit_network" "example" {
|
||||||
- `prefixes` (List of String, Deprecated) The prefixes of the network. This field is deprecated and will be removed soon, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.
|
- `prefixes` (List of String, Deprecated) The prefixes of the network. This field is deprecated and will be removed soon, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.
|
||||||
- `public_ip` (String) The public IP of the network.
|
- `public_ip` (String) The public IP of the network.
|
||||||
- `routed` (Boolean) Shows if the network is routed and therefore accessible from other networks.
|
- `routed` (Boolean) Shows if the network is routed and therefore accessible from other networks.
|
||||||
|
- `routing_table_id` (String) 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.
|
||||||
|
The ID of the routing table associated with the network.
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
|
||||||
- `default_region` (String) Region will be used as the default location for regional services. Not all services require a region, some are global
|
- `default_region` (String) Region will be used as the default location for regional services. Not all services require a region, some are global
|
||||||
- `dns_custom_endpoint` (String) Custom endpoint for the DNS service
|
- `dns_custom_endpoint` (String) Custom endpoint for the DNS service
|
||||||
- `enable_beta_resources` (Boolean) Enable beta resources. Default is false.
|
- `enable_beta_resources` (Boolean) Enable beta resources. Default is false.
|
||||||
- `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: [iam routing-tables]
|
- `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network
|
||||||
- `git_custom_endpoint` (String) Custom endpoint for the Git service
|
- `git_custom_endpoint` (String) Custom endpoint for the Git service
|
||||||
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
|
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
|
||||||
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
|
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,11 @@ resource "stackit_network" "example_non_routed_network" {
|
||||||
- `nameservers` (List of String, Deprecated) The nameservers of the network. This field is deprecated and will be removed soon, use `ipv4_nameservers` to configure the nameservers for IPv4.
|
- `nameservers` (List of String, Deprecated) The nameservers of the network. This field is deprecated and will be removed soon, use `ipv4_nameservers` to configure the nameservers for IPv4.
|
||||||
- `no_ipv4_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
- `no_ipv4_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
||||||
- `no_ipv6_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
- `no_ipv6_gateway` (Boolean) If set to `true`, the network doesn't have a gateway.
|
||||||
|
- `region` (String) Can only be used when experimental "network" is set.
|
||||||
|
The resource region. If not defined, the provider region is used.
|
||||||
- `routed` (Boolean) If set to `true`, the network is routed and therefore accessible from other networks.
|
- `routed` (Boolean) If set to `true`, the network is routed and therefore accessible from other networks.
|
||||||
|
- `routing_table_id` (String) Can only be used when experimental "network" is set.
|
||||||
|
The ID of the routing table associated with the network.
|
||||||
|
|
||||||
### Read-Only
|
### Read-Only
|
||||||
|
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -16,7 +16,7 @@ require (
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.0
|
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.6.0
|
github.com/stackitcloud/stackit-sdk-go/services/git v0.6.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0
|
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.19-alpha
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0
|
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.0
|
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.0
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -162,8 +162,8 @@ github.com/stackitcloud/stackit-sdk-go/services/git v0.6.0 h1:C+8z3MdvnTngcH9L72
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.6.0/go.mod h1:agI7SONeLR/IZL3TOgn1tDzfS63O2rWKQE8+huRjEzU=
|
github.com/stackitcloud/stackit-sdk-go/services/git v0.6.0/go.mod h1:agI7SONeLR/IZL3TOgn1tDzfS63O2rWKQE8+huRjEzU=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0 h1:7qm/Tft79wFlHomPdgjUJ9uJU8kEk+k9ficMGRoHtf0=
|
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0 h1:7qm/Tft79wFlHomPdgjUJ9uJU8kEk+k9ficMGRoHtf0=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0/go.mod h1:lUGkcbyMkd4nRBDFmKohIwlgtOZqQo4Ek5S5ajw90Xg=
|
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0/go.mod h1:lUGkcbyMkd4nRBDFmKohIwlgtOZqQo4Ek5S5ajw90Xg=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.19-alpha h1:HnQyJSXbtYzN9IhTO02zxLrcSxyauIbeJD+GTf23A50=
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha h1:m1jq6a8dbUe+suFuUNdHmM/cSehpGLUtDbK1CqLqydg=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.19-alpha/go.mod h1:Wt77ucOwpe9g/84LijU+YhWbn3vLcpkAoRy2i+FobNQ=
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha/go.mod h1:Nu1b5Phsv8plgZ51+fkxPVsU91ZJ5Ayz+cthilxdmQ8=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0 h1:Ef4SyTBjIkfwaws4mssa6AoK+OokHFtr7ZIflUpoXVE=
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0 h1:Ef4SyTBjIkfwaws4mssa6AoK+OokHFtr7ZIflUpoXVE=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0/go.mod h1:FiVhDlw9+yuTiUmnyGLn2qpsLW26w9OC4TS1y78czvg=
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0/go.mod h1:FiVhDlw9+yuTiUmnyGLn2qpsLW26w9OC4TS1y78czvg=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0 h1:QKOfaB7EcuJmBCxpFXN2K7g2ih0gQM6cyZ1VhTmtQfI=
|
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0 h1:QKOfaB7EcuJmBCxpFXN2K7g2ih0gQM6cyZ1VhTmtQfI=
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RoutingTablesExperiment = "routing-tables"
|
RoutingTablesExperiment = "routing-tables"
|
||||||
|
NetworkExperiment = "network"
|
||||||
|
IamExperiment = "iam"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AvailableExperiments = []string{"iam", RoutingTablesExperiment}
|
var AvailableExperiments = []string{IamExperiment, RoutingTablesExperiment, NetworkExperiment}
|
||||||
|
|
||||||
// Check if an experiment is valid.
|
// Check if an experiment is valid.
|
||||||
func ValidExperiment(experiment string, diags *diag.Diagnostics) bool {
|
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.
|
// Check if an experiment is enabled.
|
||||||
func CheckExperimentEnabled(ctx context.Context, data *core.ProviderData, experiment, resourceName string, resourceType core.ResourceType, diags *diag.Diagnostics) {
|
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) {
|
if !ValidExperiment(experiment, diags) {
|
||||||
errTitle := fmt.Sprintf("The experiment %s does not exist.", experiment)
|
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"
|
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)
|
diags.AddError(errTitle, errContent)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
experimentActive := slices.ContainsFunc(data.Experiments, func(e string) bool {
|
experimentActive := slices.ContainsFunc(data.Experiments, func(e string) bool {
|
||||||
return strings.EqualFold(e, experiment)
|
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)
|
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))
|
tflog.Warn(ctx, fmt.Sprintf("%s | %s", warnTitle, warnContent))
|
||||||
diags.AddWarning(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)
|
return false
|
||||||
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 AddExperimentDescription(description, experiment string, resourceType core.ResourceType) string {
|
func AddExperimentDescription(description, experiment string, resourceType core.ResourceType) string {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ func TestValidExperiment(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
args: args{
|
args: args{
|
||||||
experiment: "iam",
|
experiment: IamExperiment,
|
||||||
diags: &diag.Diagnostics{},
|
diags: &diag.Diagnostics{},
|
||||||
},
|
},
|
||||||
want: true,
|
want: true,
|
||||||
|
|
@ -64,9 +64,9 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
data: &core.ProviderData{
|
data: &core.ProviderData{
|
||||||
Experiments: []string{"iam"},
|
Experiments: []string{IamExperiment},
|
||||||
},
|
},
|
||||||
experiment: "iam",
|
experiment: IamExperiment,
|
||||||
resourceType: core.Resource,
|
resourceType: core.Resource,
|
||||||
diags: &diag.Diagnostics{},
|
diags: &diag.Diagnostics{},
|
||||||
},
|
},
|
||||||
|
|
@ -80,7 +80,7 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
||||||
data: &core.ProviderData{
|
data: &core.ProviderData{
|
||||||
Experiments: []string{},
|
Experiments: []string{},
|
||||||
},
|
},
|
||||||
experiment: "iam",
|
experiment: IamExperiment,
|
||||||
resourceType: core.Resource,
|
resourceType: core.Resource,
|
||||||
diags: &diag.Diagnostics{},
|
diags: &diag.Diagnostics{},
|
||||||
},
|
},
|
||||||
|
|
@ -92,7 +92,7 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
data: &core.ProviderData{
|
data: &core.ProviderData{
|
||||||
Experiments: []string{"iam"},
|
Experiments: []string{IamExperiment},
|
||||||
},
|
},
|
||||||
experiment: "foobar",
|
experiment: "foobar",
|
||||||
resourceType: core.Resource,
|
resourceType: core.Resource,
|
||||||
|
|
@ -101,6 +101,34 @@ func TestCheckExperimentEnabled(t *testing.T) {
|
||||||
wantDiagsErr: true,
|
wantDiagsErr: true,
|
||||||
wantDiagsWarning: false,
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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",
|
"organization",
|
||||||
}
|
}
|
||||||
|
|
||||||
// This resource is part of the "iam" experiment
|
|
||||||
var experiment = "iam"
|
|
||||||
|
|
||||||
// Ensure the implementation satisfies the expected interfaces.
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
var (
|
var (
|
||||||
_ resource.Resource = &roleAssignmentResource{}
|
_ resource.Resource = &roleAssignmentResource{}
|
||||||
|
|
@ -84,7 +81,7 @@ func (r *roleAssignmentResource) Configure(ctx context.Context, req resource.Con
|
||||||
return
|
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() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +97,7 @@ func (r *roleAssignmentResource) Configure(ctx context.Context, req resource.Con
|
||||||
// Schema defines the schema for the resource.
|
// Schema defines the schema for the resource.
|
||||||
func (r *roleAssignmentResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
func (r *roleAssignmentResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
descriptions := map[string]string{
|
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]\".",
|
"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),
|
"resource_id": fmt.Sprintf("%s Resource to assign the role to.", r.apiName),
|
||||||
"role": "Role to be assigned",
|
"role": "Role to be assigned",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -21,6 +22,8 @@ import (
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas/wait"
|
"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/core"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
@ -50,11 +53,17 @@ var (
|
||||||
//go:embed testdata/resource-network-area-max.tf
|
//go:embed testdata/resource-network-area-max.tf
|
||||||
resourceNetworkAreaMaxConfig string
|
resourceNetworkAreaMaxConfig string
|
||||||
|
|
||||||
//go:embed testdata/resource-network-min.tf
|
//go:embed testdata/resource-network-v1-min.tf
|
||||||
resourceNetworkMinConfig string
|
resourceNetworkV1MinConfig string
|
||||||
|
|
||||||
//go:embed testdata/resource-network-max.tf
|
//go:embed testdata/resource-network-v1-max.tf
|
||||||
resourceNetworkMaxConfig string
|
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
|
//go:embed testdata/resource-network-interface-min.tf
|
||||||
resourceNetworkInterfaceMinConfig string
|
resourceNetworkInterfaceMinConfig string
|
||||||
|
|
@ -83,21 +92,10 @@ var (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
keypairPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIDsPd27M449akqCtdFg2+AmRVJz6eWio0oMP9dVg7XZ"
|
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{
|
var testConfigServerVarsMin = config.Variables{
|
||||||
"project_id": config.StringVariable(testutil.ProjectId),
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||||
|
|
@ -225,31 +223,67 @@ var testConfigVolumeVarsMaxUpdated = func() config.Variables {
|
||||||
return updatedConfig
|
return updatedConfig
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var testConfigNetworkVarsMin = config.Variables{
|
var testConfigNetworkV1VarsMin = config.Variables{
|
||||||
"project_id": config.StringVariable(testutil.ProjectId),
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
"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),
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
|
||||||
"ipv4_gateway": config.StringVariable("10.2.2.1"),
|
"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": config.StringVariable("10.2.2.0/24"),
|
||||||
"ipv4_prefix_length": config.IntegerVariable(24),
|
"ipv4_prefix_length": config.IntegerVariable(24),
|
||||||
"routed": config.BoolVariable(false),
|
"routed": config.BoolVariable(false),
|
||||||
"label": config.StringVariable("label"),
|
"label": config.StringVariable("label"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var testConfigNetworkVarsMaxUpdated = func() config.Variables {
|
var testConfigNetworkV1VarsMaxUpdated = func() config.Variables {
|
||||||
updatedConfig := config.Variables{}
|
updatedConfig := config.Variables{}
|
||||||
for k, v := range testConfigNetworkVarsMax {
|
for k, v := range testConfigNetworkV1VarsMax {
|
||||||
updatedConfig[k] = v
|
updatedConfig[k] = v
|
||||||
}
|
}
|
||||||
|
updatedConfig["name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["name"])))
|
||||||
updatedConfig["ipv4_gateway"] = config.StringVariable("")
|
updatedConfig["ipv4_gateway"] = config.StringVariable("")
|
||||||
updatedConfig["ipv4_nameservers"] = config.StringVariable("10.2.2.3")
|
updatedConfig["ipv4_nameserver_0"] = config.StringVariable("10.2.2.10")
|
||||||
updatedConfig["ipv4_prefix"] = config.StringVariable("10.2.2.0/25")
|
updatedConfig["label"] = config.StringVariable("updated")
|
||||||
updatedConfig["ipv4_prefix_length"] = config.IntegerVariable(25)
|
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")
|
updatedConfig["label"] = config.StringVariable("updated")
|
||||||
return updatedConfig
|
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
|
// if no local file is provided the test should create a default file and work with this instead of failing
|
||||||
var localFileForIaasImage os.File
|
var localFileForIaasImage os.File
|
||||||
|
|
||||||
func TestAccNetworkMin(t *testing.T) {
|
func TestAccNetworkV1Min(t *testing.T) {
|
||||||
t.Logf("TestAccNetworkMin name: %s", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"]))
|
t.Logf("TestAccNetworkV1Min name: %s", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"]))
|
||||||
resource.ParallelTest(t, resource.TestCase{
|
resource.ParallelTest(t, resource.TestCase{
|
||||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||||
CheckDestroy: testAccCheckDestroy,
|
CheckDestroy: testAccCheckDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMin,
|
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMinConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkV1MinConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["project_id"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"])),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||||
|
|
@ -477,7 +511,7 @@ func TestAccNetworkMin(t *testing.T) {
|
||||||
},
|
},
|
||||||
// Data source
|
// Data source
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMin,
|
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||||
Config: fmt.Sprintf(`
|
Config: fmt.Sprintf(`
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
|
|
@ -487,12 +521,12 @@ func TestAccNetworkMin(t *testing.T) {
|
||||||
network_id = stackit_network.network.network_id
|
network_id = stackit_network.network.network_id
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
testutil.IaaSProviderConfig(), resourceNetworkMinConfig,
|
testutil.IaaSProviderConfig(), resourceNetworkV1MinConfig,
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
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", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
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", "ipv4_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "public_ip"),
|
resource.TestCheckResourceAttrSet("data.stackit_network.network", "public_ip"),
|
||||||
|
|
@ -501,7 +535,7 @@ func TestAccNetworkMin(t *testing.T) {
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMin,
|
ConfigVariables: testConfigNetworkV1VarsMin,
|
||||||
ResourceName: "stackit_network.network",
|
ResourceName: "stackit_network.network",
|
||||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||||
r, ok := s.RootModule().Resources["stackit_network.network"]
|
r, ok := s.RootModule().Resources["stackit_network.network"]
|
||||||
|
|
@ -517,8 +551,8 @@ func TestAccNetworkMin(t *testing.T) {
|
||||||
ImportState: true,
|
ImportState: true,
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["project_id"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["project_id"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMin["name"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMin["name"])),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||||
|
|
@ -530,70 +564,106 @@ func TestAccNetworkMin(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccNetworkMax(t *testing.T) {
|
func TestAccNetworkV1Max(t *testing.T) {
|
||||||
t.Logf("TestAccNetworkMax name: %s", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"]))
|
t.Logf("TestAccNetworkV1Max name: %s", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"]))
|
||||||
resource.ParallelTest(t, resource.TestCase{
|
resource.ParallelTest(t, resource.TestCase{
|
||||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||||
CheckDestroy: testAccCheckDestroy,
|
CheckDestroy: testAccCheckDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
|
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMax,
|
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMaxConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkV1MaxConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["project_id"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_gateway"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||||
resource.TestCheckNoResourceAttr("stackit_network.network", "no_ipv4_gateway"),
|
resource.TestCheckNoResourceAttr("stackit_network.network_prefix", "no_ipv4_gateway"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.#", "1"),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_nameservers"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_nameservers.1", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix_length"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "ipv4_prefixes.#", "1"),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["routed"])),
|
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["label"])),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "routed", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["routed"])),
|
||||||
resource.TestCheckNoResourceAttr("stackit_network.network", "public_ip")),
|
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
|
// Data source
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMax,
|
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||||
Config: fmt.Sprintf(`
|
Config: fmt.Sprintf(`
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
|
|
||||||
data "stackit_network" "network" {
|
data "stackit_network" "network_prefix" {
|
||||||
project_id = stackit_network.network.project_id
|
project_id = stackit_network.network_prefix.project_id
|
||||||
network_id = stackit_network.network.network_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(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "network_id"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["project_id"])),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["name"])),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["name"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_gateway"])),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.#", "1"),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_nameservers.#", "2"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.0", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_nameservers"])),
|
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix"])),
|
resource.TestCheckTypeSetElemAttr("data.stackit_network.network_prefix", "ipv4_nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["ipv4_prefix_length"])),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.#", "1"),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttr("data.stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["routed"])),
|
resource.TestCheckResourceAttrSet("data.stackit_network.network_prefix", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMax["label"])),
|
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
|
// Import
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMax,
|
ConfigVariables: testConfigNetworkV1VarsMax,
|
||||||
ResourceName: "stackit_network.network",
|
ResourceName: "stackit_network.network_prefix",
|
||||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
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 {
|
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"]
|
networkId, ok := r.Primary.Attributes["network_id"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -603,39 +673,449 @@ func TestAccNetworkMax(t *testing.T) {
|
||||||
},
|
},
|
||||||
ImportState: true,
|
ImportState: true,
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("stackit_network.network_prefix", "network_id"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "name", networkResource["name"]),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "name", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["project_id"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_gateway", networkResource["ipv4_gateway"]),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_gateway", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_gateway"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_nameservers.#", "2"),
|
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
|
// 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_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_0"])),
|
||||||
resource.TestCheckTypeSetElemAttr("stackit_network.network", "nameservers.*", networkResource["nameserver1"]),
|
resource.TestCheckTypeSetElemAttr("stackit_network.network_prefix", "nameservers.*", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_nameserver_1"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix", networkResource["ipv4_prefix"]),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefix_length", networkResource["ipv4_prefix_length"]),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefix_length", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix_length"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.#", "1"),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.#", "1"),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "ipv4_prefixes.0", networkResource["ipv4_prefix"]),
|
resource.TestCheckResourceAttr("stackit_network.network_prefix", "ipv4_prefixes.0", testutil.ConvertConfigVariable(testConfigNetworkV1VarsMax["ipv4_prefix"])),
|
||||||
resource.TestCheckResourceAttr("data.stackit_network.network", "routed", networkResource["routed"]),
|
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
|
// Update
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigNetworkVarsMaxUpdated,
|
ConfigVariables: testConfigNetworkV1VarsMaxUpdated,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceNetworkMaxConfig),
|
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(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "network_id"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["project_id"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "project_id", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["project_id"])),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["name"])),
|
resource.TestCheckResourceAttr("stackit_network.network", "name", testutil.ConvertConfigVariable(testConfigNetworkV2VarsMin["name"])),
|
||||||
resource.TestCheckNoResourceAttr("stackit_network.network", "ipv4_gateway"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv4_prefixes.#"),
|
||||||
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.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "ipv6_prefixes.#"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "routed", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["routed"])),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "public_ip"),
|
||||||
resource.TestCheckResourceAttr("stackit_network.network", "labels.acc-test", testutil.ConvertConfigVariable(testConfigNetworkVarsMaxUpdated["label"])),
|
resource.TestCheckResourceAttrSet("stackit_network.network", "region"),
|
||||||
resource.TestCheckNoResourceAttr("stackit_network.network", "public_ip"),
|
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
|
// Deletion is done by the framework implicitly
|
||||||
|
|
@ -3544,7 +4024,7 @@ func TestAccImageMax(t *testing.T) {
|
||||||
|
|
||||||
func testAccCheckDestroy(s *terraform.State) error {
|
func testAccCheckDestroy(s *terraform.State) error {
|
||||||
checkFunctions := []func(s *terraform.State) error{
|
checkFunctions := []func(s *terraform.State) error{
|
||||||
testAccCheckNetworkDestroy,
|
testAccCheckNetworkV1Destroy,
|
||||||
testAccCheckNetworkInterfaceDestroy,
|
testAccCheckNetworkInterfaceDestroy,
|
||||||
testAccCheckNetworkAreaDestroy,
|
testAccCheckNetworkAreaDestroy,
|
||||||
testAccCheckIaaSVolumeDestroy,
|
testAccCheckIaaSVolumeDestroy,
|
||||||
|
|
@ -3573,7 +4053,7 @@ func testAccCheckDestroy(s *terraform.State) error {
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckNetworkDestroy(s *terraform.State) error {
|
func testAccCheckNetworkV1Destroy(s *terraform.State) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var client *iaas.APIClient
|
var client *iaas.APIClient
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -3616,6 +4096,48 @@ func testAccCheckNetworkDestroy(s *terraform.State) error {
|
||||||
return errors.Join(errs...)
|
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 {
|
func testAccCheckNetworkInterfaceDestroy(s *terraform.State) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var client *iaas.APIClient
|
var client *iaas.APIClient
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"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/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"
|
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-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
|
@ -17,7 +19,6 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
"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/core"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -26,28 +27,6 @@ var (
|
||||||
_ datasource.DataSource = &networkDataSource{}
|
_ 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.
|
// NewNetworkDataSource is a helper function to simplify the provider implementation.
|
||||||
func NewNetworkDataSource() datasource.DataSource {
|
func NewNetworkDataSource() datasource.DataSource {
|
||||||
return &networkDataSource{}
|
return &networkDataSource{}
|
||||||
|
|
@ -56,6 +35,10 @@ func NewNetworkDataSource() datasource.DataSource {
|
||||||
// networkDataSource is the data source implementation.
|
// networkDataSource is the data source implementation.
|
||||||
type networkDataSource struct {
|
type networkDataSource struct {
|
||||||
client *iaas.APIClient
|
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.
|
// 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) {
|
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 {
|
if !ok {
|
||||||
return
|
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() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
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")
|
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.",
|
Description: "Shows if the network is routed and therefore accessible from other networks.",
|
||||||
Computed: true,
|
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.
|
// 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
|
func (d *networkDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model DataSourceModel
|
if !d.isExperimental {
|
||||||
diags := req.Config.Get(ctx, &model)
|
v1network.DatasourceRead(ctx, req, resp, d.client)
|
||||||
resp.Diagnostics.Append(diags...)
|
} else {
|
||||||
if resp.Diagnostics.HasError() {
|
v2network.DatasourceRead(ctx, req, resp, d.alphaClient, d.providerData)
|
||||||
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 := 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 (
|
import (
|
||||||
"context"
|
"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/resourcevalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
|
"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/listplanmodifier"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"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"
|
||||||
"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/conversion"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
"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/utils"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||||
)
|
)
|
||||||
|
|
@ -38,30 +37,6 @@ var (
|
||||||
_ resource.ResourceWithImportState = &networkResource{}
|
_ 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.
|
// NewNetworkResource is a helper function to simplify the provider implementation.
|
||||||
func NewNetworkResource() resource.Resource {
|
func NewNetworkResource() resource.Resource {
|
||||||
return &networkResource{}
|
return &networkResource{}
|
||||||
|
|
@ -70,6 +45,10 @@ func NewNetworkResource() resource.Resource {
|
||||||
// networkResource is the resource implementation.
|
// networkResource is the resource implementation.
|
||||||
type networkResource struct {
|
type networkResource struct {
|
||||||
client *iaas.APIClient
|
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.
|
// 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.
|
// Configure adds the provider configured client to the resource.
|
||||||
func (r *networkResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
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 {
|
if !ok {
|
||||||
return
|
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() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
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")
|
tflog.Info(ctx, "IaaS client configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r networkResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
|
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||||
var model Model
|
// Use the modifier to set the effective region in the current plan.
|
||||||
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
|
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() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
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.")
|
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
|
// ConfigValidators validates the resource configuration
|
||||||
|
|
@ -115,6 +150,22 @@ func (r *networkResource) ConfigValidators(_ context.Context) []resource.ConfigV
|
||||||
path.MatchRoot("no_ipv6_gateway"),
|
path.MatchRoot("no_ipv6_gateway"),
|
||||||
path.MatchRoot("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(),
|
validate.CIDR(),
|
||||||
},
|
},
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{
|
||||||
stringplanmodifier.RequiresReplace(),
|
stringplanmodifier.RequiresReplaceIfConfigured(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ipv4_prefix_length": schema.Int64Attribute{
|
"ipv4_prefix_length": schema.Int64Attribute{
|
||||||
Description: "The IPv4 prefix length of the network.",
|
Description: "The IPv4 prefix length of the network.",
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
PlanModifiers: []planmodifier.Int64{
|
||||||
|
int64planmodifier.RequiresReplaceIfConfigured(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"prefixes": schema.ListAttribute{
|
"prefixes": schema.ListAttribute{
|
||||||
Description: "The prefixes of the network. This field is deprecated and will be removed soon, use `ipv4_prefixes` to read the prefixes of the IPv4 networks.",
|
Description: "The prefixes of the network. This field is deprecated and will be removed 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(),
|
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.
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
func (r *networkResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *networkResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
if !r.isExperimental {
|
||||||
var model Model
|
v1network.Create(ctx, req, resp, r.client)
|
||||||
diags := req.Plan.Get(ctx, &model)
|
} else {
|
||||||
resp.Diagnostics.Append(diags...)
|
v2network.Create(ctx, req, resp, r.alphaClient)
|
||||||
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 := 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.
|
// 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
|
func (r *networkResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
if !r.isExperimental {
|
||||||
diags := req.State.Get(ctx, &model)
|
v1network.Read(ctx, req, resp, r.client)
|
||||||
resp.Diagnostics.Append(diags...)
|
} else {
|
||||||
if resp.Diagnostics.HasError() {
|
v2network.Read(ctx, req, resp, r.alphaClient, r.providerData)
|
||||||
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 := 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.
|
// 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
|
func (r *networkResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
if !r.isExperimental {
|
||||||
var model Model
|
v1network.Update(ctx, req, resp, r.client)
|
||||||
diags := req.Plan.Get(ctx, &model)
|
} else {
|
||||||
resp.Diagnostics.Append(diags...)
|
v2network.Update(ctx, req, resp, r.alphaClient)
|
||||||
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 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.
|
// 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
|
func (r *networkResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from state
|
if !r.isExperimental {
|
||||||
var model Model
|
v1network.Delete(ctx, req, resp, r.client)
|
||||||
diags := req.State.Get(ctx, &model)
|
} else {
|
||||||
resp.Diagnostics.Append(diags...)
|
v2network.Delete(ctx, req, resp, r.alphaClient)
|
||||||
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 := 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.
|
// ImportState imports a resource into the Terraform state on success.
|
||||||
// The expected format of the resource import identifier is: project_id,network_id
|
// 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) {
|
func (r *networkResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||||
idParts := strings.Split(req.ID, core.Separator)
|
if !r.isExperimental {
|
||||||
|
v1network.ImportState(ctx, req, resp)
|
||||||
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
|
} else {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
v2network.ImportState(ctx, req, resp)
|
||||||
"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 *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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -9,19 +9,20 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
"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) {
|
func TestMapDataSourceFields(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
state DataSourceModel
|
state networkModel.DataSourceModel
|
||||||
input *iaas.Network
|
input *iaas.Network
|
||||||
expected DataSourceModel
|
expected networkModel.DataSourceModel
|
||||||
isValid bool
|
isValid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"id_ok",
|
"id_ok",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
|
|
@ -29,7 +30,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
NetworkId: utils.Ptr("nid"),
|
NetworkId: utils.Ptr("nid"),
|
||||||
Gateway: iaas.NewNullableString(nil),
|
Gateway: iaas.NewNullableString(nil),
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -54,7 +55,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"values_ok",
|
"values_ok",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
|
|
@ -85,7 +86,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||||
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -130,7 +131,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_nameservers_changed_outside_tf",
|
"ipv4_nameservers_changed_outside_tf",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -149,7 +150,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
"ns3",
|
"ns3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -172,7 +173,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_nameservers_changed_outside_tf",
|
"ipv6_nameservers_changed_outside_tf",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -187,7 +188,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
"ns3",
|
"ns3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -207,7 +208,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_prefixes_changed_outside_tf",
|
"ipv4_prefixes_changed_outside_tf",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -222,7 +223,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
"10.100.10.0/16",
|
"10.100.10.0/16",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -248,7 +249,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_prefixes_changed_outside_tf",
|
"ipv6_prefixes_changed_outside_tf",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -263,7 +264,7 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
"fd12:3456:789a:4::/64",
|
"fd12:3456:789a:4::/64",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -286,14 +287,14 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_ipv6_gateway_nil",
|
"ipv4_ipv6_gateway_nil",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
&iaas.Network{
|
&iaas.Network{
|
||||||
NetworkId: utils.Ptr("nid"),
|
NetworkId: utils.Ptr("nid"),
|
||||||
},
|
},
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -316,18 +317,18 @@ func TestMapDataSourceFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"response_nil_fail",
|
"response_nil_fail",
|
||||||
DataSourceModel{},
|
networkModel.DataSourceModel{},
|
||||||
nil,
|
nil,
|
||||||
DataSourceModel{},
|
networkModel.DataSourceModel{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"no_resource_id",
|
"no_resource_id",
|
||||||
DataSourceModel{
|
networkModel.DataSourceModel{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
},
|
},
|
||||||
&iaas.Network{},
|
&iaas.Network{},
|
||||||
DataSourceModel{},
|
networkModel.DataSourceModel{},
|
||||||
false,
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -9,19 +9,20 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
"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) {
|
func TestMapFields(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
state Model
|
state model.Model
|
||||||
input *iaas.Network
|
input *iaas.Network
|
||||||
expected Model
|
expected model.Model
|
||||||
isValid bool
|
isValid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"id_ok",
|
"id_ok",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
|
|
@ -29,7 +30,7 @@ func TestMapFields(t *testing.T) {
|
||||||
NetworkId: utils.Ptr("nid"),
|
NetworkId: utils.Ptr("nid"),
|
||||||
Gateway: iaas.NewNullableString(nil),
|
Gateway: iaas.NewNullableString(nil),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -54,7 +55,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"values_ok",
|
"values_ok",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
|
|
@ -85,7 +86,7 @@ func TestMapFields(t *testing.T) {
|
||||||
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
Gateway: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||||
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
Gatewayv6: iaas.NewNullableString(utils.Ptr("gateway")),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -130,7 +131,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_nameservers_changed_outside_tf",
|
"ipv4_nameservers_changed_outside_tf",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -149,7 +150,7 @@ func TestMapFields(t *testing.T) {
|
||||||
"ns3",
|
"ns3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -172,7 +173,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_nameservers_changed_outside_tf",
|
"ipv6_nameservers_changed_outside_tf",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -187,7 +188,7 @@ func TestMapFields(t *testing.T) {
|
||||||
"ns3",
|
"ns3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -207,7 +208,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_prefixes_changed_outside_tf",
|
"ipv4_prefixes_changed_outside_tf",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -222,7 +223,7 @@ func TestMapFields(t *testing.T) {
|
||||||
"192.168.55.0/24",
|
"192.168.55.0/24",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -248,7 +249,7 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_prefixes_changed_outside_tf",
|
"ipv6_prefixes_changed_outside_tf",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Prefixes: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
|
|
@ -263,7 +264,7 @@ func TestMapFields(t *testing.T) {
|
||||||
"fd12:3456:789a:2::/64",
|
"fd12:3456:789a:2::/64",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -286,14 +287,14 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_ipv6_gateway_nil",
|
"ipv4_ipv6_gateway_nil",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
},
|
},
|
||||||
&iaas.Network{
|
&iaas.Network{
|
||||||
NetworkId: utils.Ptr("nid"),
|
NetworkId: utils.Ptr("nid"),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
Id: types.StringValue("pid,nid"),
|
Id: types.StringValue("pid,nid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
|
|
@ -316,18 +317,18 @@ func TestMapFields(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"response_nil_fail",
|
"response_nil_fail",
|
||||||
Model{},
|
model.Model{},
|
||||||
nil,
|
nil,
|
||||||
Model{},
|
model.Model{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"no_resource_id",
|
"no_resource_id",
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
},
|
},
|
||||||
&iaas.Network{},
|
&iaas.Network{},
|
||||||
Model{},
|
model.Model{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -353,13 +354,13 @@ func TestMapFields(t *testing.T) {
|
||||||
func TestToCreatePayload(t *testing.T) {
|
func TestToCreatePayload(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
input *Model
|
input *model.Model
|
||||||
expected *iaas.CreateNetworkPayload
|
expected *iaas.CreateNetworkPayload
|
||||||
isValid bool
|
isValid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"default_ok",
|
"default_ok",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -395,7 +396,7 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_nameservers_okay",
|
"ipv4_nameservers_okay",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -431,7 +432,7 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_default_ok",
|
"ipv6_default_ok",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -488,14 +489,14 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
func TestToUpdatePayload(t *testing.T) {
|
func TestToUpdatePayload(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
description string
|
description string
|
||||||
input *Model
|
input *model.Model
|
||||||
state Model
|
state model.Model
|
||||||
expected *iaas.PartialUpdateNetworkPayload
|
expected *iaas.PartialUpdateNetworkPayload
|
||||||
isValid bool
|
isValid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"default_ok",
|
"default_ok",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -507,7 +508,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
Routed: types.BoolValue(true),
|
Routed: types.BoolValue(true),
|
||||||
IPv4Gateway: types.StringValue("gateway"),
|
IPv4Gateway: types.StringValue("gateway"),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Labels: types.MapNull(types.StringType),
|
Labels: types.MapNull(types.StringType),
|
||||||
|
|
@ -531,7 +532,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_nameservers_okay",
|
"ipv4_nameservers_okay",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -543,7 +544,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
Routed: types.BoolValue(true),
|
Routed: types.BoolValue(true),
|
||||||
IPv4Gateway: types.StringValue("gateway"),
|
IPv4Gateway: types.StringValue("gateway"),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Labels: types.MapNull(types.StringType),
|
Labels: types.MapNull(types.StringType),
|
||||||
|
|
@ -567,7 +568,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv4_gateway_nil",
|
"ipv4_gateway_nil",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv4Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -578,7 +579,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Routed: types.BoolValue(true),
|
Routed: types.BoolValue(true),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Labels: types.MapNull(types.StringType),
|
Labels: types.MapNull(types.StringType),
|
||||||
|
|
@ -601,7 +602,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_default_ok",
|
"ipv6_default_ok",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -613,7 +614,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
Routed: types.BoolValue(true),
|
Routed: types.BoolValue(true),
|
||||||
IPv6Gateway: types.StringValue("gateway"),
|
IPv6Gateway: types.StringValue("gateway"),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Labels: types.MapNull(types.StringType),
|
Labels: types.MapNull(types.StringType),
|
||||||
|
|
@ -637,7 +638,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ipv6_gateway_nil",
|
"ipv6_gateway_nil",
|
||||||
&Model{
|
&model.Model{
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
IPv6Nameservers: types.ListValueMust(types.StringType, []attr.Value{
|
||||||
types.StringValue("ns1"),
|
types.StringValue("ns1"),
|
||||||
|
|
@ -648,7 +649,7 @@ func TestToUpdatePayload(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Routed: types.BoolValue(true),
|
Routed: types.BoolValue(true),
|
||||||
},
|
},
|
||||||
Model{
|
model.Model{
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
NetworkId: types.StringValue("nid"),
|
NetworkId: types.StringValue("nid"),
|
||||||
Labels: types.MapNull(types.StringType),
|
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 "project_id" {}
|
||||||
variable "name" {}
|
variable "name" {}
|
||||||
variable "ipv4_gateway" {}
|
variable "ipv4_gateway" {}
|
||||||
variable "ipv4_nameservers" {}
|
variable "ipv4_nameserver_0" {}
|
||||||
|
variable "ipv4_nameserver_1" {}
|
||||||
variable "ipv4_prefix" {}
|
variable "ipv4_prefix" {}
|
||||||
variable "ipv4_prefix_length" {}
|
variable "ipv4_prefix_length" {}
|
||||||
variable "routed" {}
|
variable "routed" {}
|
||||||
variable "label" {}
|
variable "label" {}
|
||||||
|
|
||||||
resource "stackit_network" "network" {
|
resource "stackit_network" "network_prefix" {
|
||||||
project_id = var.project_id
|
project_id = var.project_id
|
||||||
name = var.name
|
name = var.name
|
||||||
ipv4_gateway = var.ipv4_gateway != "" ? var.ipv4_gateway : null
|
ipv4_gateway = var.ipv4_gateway != "" ? var.ipv4_gateway : null
|
||||||
no_ipv4_gateway = var.ipv4_gateway != "" ? null : true
|
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
|
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
|
ipv4_prefix_length = var.ipv4_prefix_length
|
||||||
routed = var.routed
|
routed = var.routed
|
||||||
labels = {
|
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"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -19,8 +20,6 @@ import (
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
"github.com/stackitcloud/stackit-sdk-go/services/iaasalpha"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||||
|
|
||||||
"maps"
|
|
||||||
|
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,7 +122,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableMin,
|
ConfigVariables: testConfigRoutingTableMin,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMin["organization_id"])),
|
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
|
network_area_id = stackit_routing_table.routing_table.network_area_id
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig,
|
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig,
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
|
|
@ -232,7 +231,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Update
|
// Update
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableMinUpdated,
|
ConfigVariables: testConfigRoutingTableMinUpdated,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMinConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMinConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMinUpdated["organization_id"])),
|
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMinUpdated["organization_id"])),
|
||||||
|
|
@ -261,7 +260,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableMax,
|
ConfigVariables: testConfigRoutingTableMax,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMax["organization_id"])),
|
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
|
network_area_id = stackit_routing_table.routing_table.network_area_id
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig,
|
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig,
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
|
|
@ -373,7 +372,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Update
|
// Update
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableMaxUpdated,
|
ConfigVariables: testConfigRoutingTableMaxUpdated,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableMaxConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableMaxConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMaxUpdated["organization_id"])),
|
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableMaxUpdated["organization_id"])),
|
||||||
|
|
@ -403,7 +402,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableRouteMin,
|
ConfigVariables: testConfigRoutingTableRouteMin,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMin["organization_id"])),
|
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
|
routing_table_id = stackit_routing_table_route.route.routing_table_id
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig,
|
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig,
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table route
|
// Routing table route
|
||||||
|
|
@ -529,7 +528,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Update
|
// Update
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableRouteMinUpdated,
|
ConfigVariables: testConfigRoutingTableRouteMinUpdated,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMinConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMinConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMinUpdated["organization_id"])),
|
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMinUpdated["organization_id"])),
|
||||||
|
|
@ -569,7 +568,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Creation
|
// Creation
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableRouteMax,
|
ConfigVariables: testConfigRoutingTableRouteMax,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMax["organization_id"])),
|
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
|
routing_table_id = stackit_routing_table_route.route.routing_table_id
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig,
|
testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig,
|
||||||
),
|
),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table route
|
// Routing table route
|
||||||
|
|
@ -698,7 +697,7 @@ func TestAccRoutingTable(t *testing.T) {
|
||||||
// Update
|
// Update
|
||||||
{
|
{
|
||||||
ConfigVariables: testConfigRoutingTableRouteMaxUpdated,
|
ConfigVariables: testConfigRoutingTableRouteMaxUpdated,
|
||||||
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfig(), resourceRoutingTableRouteMaxConfig),
|
Config: fmt.Sprintf("%s\n%s", testutil.IaaSProviderConfigWithExperiments(), resourceRoutingTableRouteMaxConfig),
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
// Routing table
|
// Routing table
|
||||||
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMaxUpdated["organization_id"])),
|
resource.TestCheckResourceAttr("stackit_routing_table.routing_table", "organization_id", testutil.ConvertConfigVariable(testConfigRoutingTableRouteMaxUpdated["organization_id"])),
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,28 @@ func IaaSProviderConfig() string {
|
||||||
return `
|
return `
|
||||||
provider "stackit" {
|
provider "stackit" {
|
||||||
default_region = "eu01"
|
default_region = "eu01"
|
||||||
experiments = ["routing-tables"]
|
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
provider "stackit" {
|
provider "stackit" {
|
||||||
iaas_custom_endpoint = "%s"
|
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,
|
IaaSCustomEndpoint,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package stackit
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"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",
|
"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",
|
"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.",
|
"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{
|
resp.Schema = schema.Schema{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue