Feat/separate functions (#19)
* chore: work save * fix: refactor flavors * fix: refactor pg user and database * fix: refactor flavor parameters * fix: refactor tf script * chore: work save * chore: work save * chore: work save --------- Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
This commit is contained in:
parent
910551f09d
commit
0150fea302
54 changed files with 6010 additions and 2826 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -15,9 +15,9 @@ bin/
|
||||||
**/terraform.tfstate**
|
**/terraform.tfstate**
|
||||||
.terraform.lock.hcl
|
.terraform.lock.hcl
|
||||||
.terraform.tfstate.lock.info
|
.terraform.tfstate.lock.info
|
||||||
main.tf
|
**/config.tfrc
|
||||||
example.tf
|
**/variables.tf
|
||||||
index.tf
|
**/service_account.json
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
|
||||||
43
docs/data-sources/postgresflexalpha_flavor.md
Normal file
43
docs/data-sources/postgresflexalpha_flavor.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||||
|
page_title: "stackitprivatepreview_postgresflexalpha_flavor Data Source - stackitprivatepreview"
|
||||||
|
subcategory: ""
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# stackitprivatepreview_postgresflexalpha_flavor (Data Source)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- schema generated by tfplugindocs -->
|
||||||
|
## Schema
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
- `cpu` (Number) The cpu count of the instance.
|
||||||
|
- `node_type` (String) defines the nodeType it can be either single or replica
|
||||||
|
- `project_id` (String) The cpu count of the instance.
|
||||||
|
- `ram` (Number) The memory of the instance in Gibibyte.
|
||||||
|
- `region` (String) The flavor description.
|
||||||
|
- `storage_class` (String) The memory of the instance in Gibibyte.
|
||||||
|
|
||||||
|
### Read-Only
|
||||||
|
|
||||||
|
- `description` (String) The flavor description.
|
||||||
|
- `flavor_id` (String) The flavor id of the instance flavor.
|
||||||
|
- `id` (String) The terraform id of the instance flavor.
|
||||||
|
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
|
||||||
|
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
|
||||||
|
- `storage_classes` (Attributes List) (see [below for nested schema](#nestedatt--storage_classes))
|
||||||
|
|
||||||
|
<a id="nestedatt--storage_classes"></a>
|
||||||
|
### Nested Schema for `storage_classes`
|
||||||
|
|
||||||
|
Read-Only:
|
||||||
|
|
||||||
|
- `class` (String)
|
||||||
|
- `max_io_per_sec` (Number)
|
||||||
|
- `max_through_in_mb` (Number)
|
||||||
|
|
@ -35,11 +35,12 @@ data "stackitprivatepreview_postgresflexalpha_instance" "example" {
|
||||||
|
|
||||||
- `backup_schedule` (String)
|
- `backup_schedule` (String)
|
||||||
- `encryption` (Attributes) (see [below for nested schema](#nestedatt--encryption))
|
- `encryption` (Attributes) (see [below for nested schema](#nestedatt--encryption))
|
||||||
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor))
|
- `flavor_id` (String)
|
||||||
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`".
|
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`".
|
||||||
- `name` (String) Instance name.
|
- `name` (String) Instance name.
|
||||||
- `network` (Attributes) (see [below for nested schema](#nestedatt--network))
|
- `network` (Attributes) (see [below for nested schema](#nestedatt--network))
|
||||||
- `replicas` (Number)
|
- `replicas` (Number)
|
||||||
|
- `retention_days` (Number)
|
||||||
- `storage` (Attributes) (see [below for nested schema](#nestedatt--storage))
|
- `storage` (Attributes) (see [below for nested schema](#nestedatt--storage))
|
||||||
- `version` (String)
|
- `version` (String)
|
||||||
|
|
||||||
|
|
@ -54,18 +55,6 @@ Read-Only:
|
||||||
- `service_account` (String)
|
- `service_account` (String)
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--flavor"></a>
|
|
||||||
### Nested Schema for `flavor`
|
|
||||||
|
|
||||||
Read-Only:
|
|
||||||
|
|
||||||
- `cpu` (Number)
|
|
||||||
- `description` (String)
|
|
||||||
- `id` (String)
|
|
||||||
- `node_type` (String)
|
|
||||||
- `ram` (Number)
|
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--network"></a>
|
<a id="nestedatt--network"></a>
|
||||||
### Nested Schema for `network`
|
### Nested Schema for `network`
|
||||||
|
|
||||||
|
|
|
||||||
43
docs/data-sources/sqlserverflexalpha_flavor.md
Normal file
43
docs/data-sources/sqlserverflexalpha_flavor.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||||
|
page_title: "stackitprivatepreview_sqlserverflexalpha_flavor Data Source - stackitprivatepreview"
|
||||||
|
subcategory: ""
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# stackitprivatepreview_sqlserverflexalpha_flavor (Data Source)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- schema generated by tfplugindocs -->
|
||||||
|
## Schema
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
- `cpu` (Number) The cpu count of the instance.
|
||||||
|
- `node_type` (String) defines the nodeType it can be either single or replica
|
||||||
|
- `project_id` (String) The cpu count of the instance.
|
||||||
|
- `ram` (Number) The memory of the instance in Gibibyte.
|
||||||
|
- `region` (String) The flavor description.
|
||||||
|
- `storage_class` (String) The memory of the instance in Gibibyte.
|
||||||
|
|
||||||
|
### Read-Only
|
||||||
|
|
||||||
|
- `description` (String) The flavor description.
|
||||||
|
- `flavor_id` (String) The flavor id of the instance flavor.
|
||||||
|
- `id` (String) The terraform id of the instance flavor.
|
||||||
|
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
|
||||||
|
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
|
||||||
|
- `storage_classes` (Attributes List) (see [below for nested schema](#nestedatt--storage_classes))
|
||||||
|
|
||||||
|
<a id="nestedatt--storage_classes"></a>
|
||||||
|
### Nested Schema for `storage_classes`
|
||||||
|
|
||||||
|
Read-Only:
|
||||||
|
|
||||||
|
- `class` (String)
|
||||||
|
- `max_io_per_sec` (Number)
|
||||||
|
- `max_through_in_mb` (Number)
|
||||||
|
|
@ -1,16 +1,31 @@
|
||||||
---
|
---
|
||||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||||
page_title: "stackitprivatepreview_postgresflex_database Resource - stackitprivatepreview"
|
page_title: "stackitprivatepreview_postgresflexalpha_database Resource - stackitprivatepreview"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
|
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
|
||||||
---
|
---
|
||||||
|
|
||||||
# stackitprivatepreview_postgresflex_database (Resource)
|
# stackitprivatepreview_postgresflexalpha_database (Resource)
|
||||||
|
|
||||||
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
|
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```terraform
|
||||||
|
resource "stackitprivatepreview_postgresflexalpha_database" "example" {
|
||||||
|
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
name = "mydb"
|
||||||
|
owner = "myusername"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only use the import statement, if you want to import an existing postgresflex database
|
||||||
|
import {
|
||||||
|
to = stackitprivatepreview_postgresflexalpha_database.import-example
|
||||||
|
id = "${var.project_id},${var.region},${var.postgres_instance_id},${var.postgres_database_id}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<!-- schema generated by tfplugindocs -->
|
<!-- schema generated by tfplugindocs -->
|
||||||
## Schema
|
## Schema
|
||||||
|
|
@ -28,5 +43,5 @@ Postgres Flex database resource schema. Must have a `region` specified in the pr
|
||||||
|
|
||||||
### Read-Only
|
### Read-Only
|
||||||
|
|
||||||
- `database_id` (String) Database ID.
|
- `database_id` (Number) Database ID.
|
||||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`database_id`".
|
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`database_id`".
|
||||||
|
|
@ -44,7 +44,7 @@ import {
|
||||||
|
|
||||||
- `backup_schedule` (String)
|
- `backup_schedule` (String)
|
||||||
- `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption))
|
- `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption))
|
||||||
- `flavor` (Attributes) The block that defines the flavor data. (see [below for nested schema](#nestedatt--flavor))
|
- `flavor_id` (String)
|
||||||
- `name` (String) Instance name.
|
- `name` (String) Instance name.
|
||||||
- `network` (Attributes) The network block configuration. (see [below for nested schema](#nestedatt--network))
|
- `network` (Attributes) The network block configuration. (see [below for nested schema](#nestedatt--network))
|
||||||
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
||||||
|
|
@ -73,24 +73,6 @@ Required:
|
||||||
- `service_account` (String) The service account ID of the service account.
|
- `service_account` (String) The service account ID of the service account.
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--flavor"></a>
|
|
||||||
### Nested Schema for `flavor`
|
|
||||||
|
|
||||||
Required:
|
|
||||||
|
|
||||||
- `cpu` (Number) The CPU count of the flavor.
|
|
||||||
- `ram` (Number) The RAM count of the flavor.
|
|
||||||
|
|
||||||
Optional:
|
|
||||||
|
|
||||||
- `id` (String) The ID of the flavor.
|
|
||||||
- `node_type` (String) The node type of the flavor. (Single or Replicas)
|
|
||||||
|
|
||||||
Read-Only:
|
|
||||||
|
|
||||||
- `description` (String) The flavor detailed flavor name.
|
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--network"></a>
|
<a id="nestedatt--network"></a>
|
||||||
### Nested Schema for `network`
|
### Nested Schema for `network`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,7 @@ import {
|
||||||
### Read-Only
|
### Read-Only
|
||||||
|
|
||||||
- `connection_string` (String)
|
- `connection_string` (String)
|
||||||
- `host` (String)
|
|
||||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
|
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
|
||||||
- `password` (String, Sensitive)
|
- `password` (String, Sensitive)
|
||||||
- `port` (Number)
|
|
||||||
- `status` (String)
|
- `status` (String)
|
||||||
- `uri` (String, Sensitive)
|
|
||||||
- `user_id` (Number) User ID.
|
- `user_id` (Number) User ID.
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ import {
|
||||||
### Required
|
### Required
|
||||||
|
|
||||||
- `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption))
|
- `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption))
|
||||||
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor))
|
- `flavor_id` (String)
|
||||||
- `name` (String) Instance name.
|
- `name` (String) Instance name.
|
||||||
- `network` (Attributes) The network block. (see [below for nested schema](#nestedatt--network))
|
- `network` (Attributes) The network block. (see [below for nested schema](#nestedatt--network))
|
||||||
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
||||||
|
|
@ -75,30 +75,12 @@ Required:
|
||||||
- `service_account` (String)
|
- `service_account` (String)
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--flavor"></a>
|
|
||||||
### Nested Schema for `flavor`
|
|
||||||
|
|
||||||
Required:
|
|
||||||
|
|
||||||
- `cpu` (Number)
|
|
||||||
- `node_type` (String)
|
|
||||||
- `ram` (Number)
|
|
||||||
|
|
||||||
Optional:
|
|
||||||
|
|
||||||
- `id` (String)
|
|
||||||
|
|
||||||
Read-Only:
|
|
||||||
|
|
||||||
- `description` (String)
|
|
||||||
|
|
||||||
|
|
||||||
<a id="nestedatt--network"></a>
|
<a id="nestedatt--network"></a>
|
||||||
### Nested Schema for `network`
|
### Nested Schema for `network`
|
||||||
|
|
||||||
Required:
|
Required:
|
||||||
|
|
||||||
- `access_scope` (String) The access scope of the instance. (e.g. SNA)
|
- `access_scope` (String) The access scope of the instance. (SNA | PUBLIC)
|
||||||
- `acl` (List of String) The Access Control List (ACL) for the SQLServer Flex instance.
|
- `acl` (List of String) The Access Control List (ACL) for the SQLServer Flex instance.
|
||||||
|
|
||||||
Read-Only:
|
Read-Only:
|
||||||
|
|
|
||||||
20
go.mod
20
go.mod
|
|
@ -12,22 +12,20 @@ require (
|
||||||
github.com/hashicorp/terraform-plugin-testing v1.14.0
|
github.com/hashicorp/terraform-plugin-testing v1.14.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/core v0.20.1
|
github.com/stackitcloud/stackit-sdk-go/core v0.20.1
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.0
|
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.0
|
||||||
github.com/stretchr/testify v1.10.0
|
|
||||||
github.com/teambition/rrule-go v1.8.2
|
github.com/teambition/rrule-go v1.8.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc // indirect
|
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||||
github.com/agext/levenshtein v1.2.3 // indirect
|
github.com/agext/levenshtein v1.2.3 // indirect
|
||||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
|
@ -57,23 +55,21 @@ require (
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/oklog/run v1.2.0 // indirect
|
github.com/oklog/run v1.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/zclconf/go-cty v1.17.0 // indirect
|
github.com/zclconf/go-cty v1.17.0 // indirect
|
||||||
golang.org/x/crypto v0.46.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/mod v0.31.0 // indirect
|
golang.org/x/mod v0.32.0 // indirect
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.33.0 // indirect
|
||||||
golang.org/x/tools v0.40.0 // indirect
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
|
||||||
google.golang.org/grpc v1.77.0 // indirect
|
google.golang.org/grpc v1.78.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tool golang.org/x/tools/cmd/goimports
|
tool golang.org/x/tools/cmd/goimports
|
||||||
|
|
|
||||||
32
go.sum
32
go.sum
|
|
@ -11,8 +11,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
|
||||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
|
||||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||||
|
|
@ -68,8 +68,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
|
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
|
||||||
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
|
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
|
@ -188,8 +188,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
|
@ -212,10 +212,10 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA=
|
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo=
|
||||||
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
|
|
@ -224,8 +224,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|
@ -238,10 +238,10 @@ gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ func CreateInstanceWaitHandler(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tflog.Info(ctx, "Waiting for instance (calling list users")
|
||||||
// // User operations aren't available right after an instance is deemed successful
|
// // User operations aren't available right after an instance is deemed successful
|
||||||
// // To check if they are, perform a users request
|
// // To check if they are, perform a users request
|
||||||
_, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId)
|
_, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId)
|
||||||
|
|
@ -114,7 +115,7 @@ func CreateInstanceWaitHandler(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
// Sleep before wait is set because sometimes API returns 404 right after creation request
|
// Sleep before wait is set because sometimes API returns 404 right after creation request
|
||||||
handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(15 * time.Second)
|
handler.SetTimeout(90 * time.Minute).SetSleepBeforeWait(30 * time.Second)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,72 +155,72 @@ func PartialUpdateInstanceWaitHandler(
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteInstanceWaitHandler will wait for instance deletion
|
//// DeleteInstanceWaitHandler will wait for instance deletion
|
||||||
func DeleteInstanceWaitHandler(
|
//func DeleteInstanceWaitHandler(
|
||||||
ctx context.Context,
|
// ctx context.Context,
|
||||||
a APIClientInstanceInterface,
|
// a APIClientInstanceInterface,
|
||||||
projectId, region, instanceId string,
|
// projectId, region, instanceId string,
|
||||||
) *wait.AsyncActionHandler[struct{}] {
|
//) *wait.AsyncActionHandler[struct{}] {
|
||||||
handler := wait.New(
|
// handler := wait.New(
|
||||||
func() (waitFinished bool, response *struct{}, err error) {
|
// func() (waitFinished bool, response *struct{}, err error) {
|
||||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
// s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return false, nil, err
|
// return false, nil, err
|
||||||
}
|
// }
|
||||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
// if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||||
return false, nil, nil
|
// return false, nil, nil
|
||||||
}
|
// }
|
||||||
// TODO - maybe we want to validate status if no 404 error (only unknown or terminating should be valid)
|
// // TODO - maybe we want to validate status if no 404 error (only unknown or terminating should be valid)
|
||||||
// switch *s.Status {
|
// // switch *s.Status {
|
||||||
// default:
|
// // default:
|
||||||
// return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
// // return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||||
// case InstanceStateSuccess:
|
// // case InstanceStateSuccess:
|
||||||
// return false, nil, nil
|
// // return false, nil, nil
|
||||||
// case InstanceStateTerminating:
|
// // case InstanceStateTerminating:
|
||||||
// return false, nil, nil
|
// // return false, nil, nil
|
||||||
// }
|
// // }
|
||||||
|
//
|
||||||
// TODO - add tflog for ignored cases
|
// // TODO - add tflog for ignored cases
|
||||||
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
|
// 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 {
|
// if !ok {
|
||||||
return false, nil, err
|
// return false, nil, err
|
||||||
}
|
// }
|
||||||
if oapiErr.StatusCode != 404 {
|
// if oapiErr.StatusCode != 404 {
|
||||||
return false, nil, err
|
// return false, nil, err
|
||||||
}
|
// }
|
||||||
return true, nil, nil
|
// return true, nil, nil
|
||||||
},
|
// },
|
||||||
)
|
// )
|
||||||
handler.SetTimeout(5 * time.Minute)
|
// handler.SetTimeout(5 * time.Minute)
|
||||||
return handler
|
// return handler
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// TODO - remove
|
//// TODO - remove
|
||||||
// ForceDeleteInstanceWaitHandler will wait for instance deletion
|
//// ForceDeleteInstanceWaitHandler will wait for instance deletion
|
||||||
func ForceDeleteInstanceWaitHandler(
|
//func ForceDeleteInstanceWaitHandler(
|
||||||
ctx context.Context,
|
// ctx context.Context,
|
||||||
a APIClientInstanceInterface,
|
// a APIClientInstanceInterface,
|
||||||
projectId, region, instanceId string,
|
// projectId, region, instanceId string,
|
||||||
) *wait.AsyncActionHandler[struct{}] {
|
//) *wait.AsyncActionHandler[struct{}] {
|
||||||
handler := wait.New(
|
// handler := wait.New(
|
||||||
func() (waitFinished bool, response *struct{}, err error) {
|
// func() (waitFinished bool, response *struct{}, err error) {
|
||||||
_, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
// _, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
return false, nil, nil
|
// return false, nil, 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
|
// 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 {
|
// if !ok {
|
||||||
return false, nil, err
|
// return false, nil, err
|
||||||
}
|
// }
|
||||||
if oapiErr.StatusCode != 404 {
|
// if oapiErr.StatusCode != 404 {
|
||||||
return false, nil, err
|
// return false, nil, err
|
||||||
}
|
// }
|
||||||
return true, nil, nil
|
// return true, nil, nil
|
||||||
},
|
// },
|
||||||
)
|
// )
|
||||||
handler.SetTimeout(15 * time.Minute)
|
// handler.SetTimeout(15 * time.Minute)
|
||||||
return handler
|
// return handler
|
||||||
}
|
//}
|
||||||
|
|
||||||
// DeleteUserWaitHandler will wait for delete
|
// DeleteUserWaitHandler will wait for delete
|
||||||
func DeleteUserWaitHandler(
|
func DeleteUserWaitHandler(
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
# see other files
|
|
||||||
3
sample/postgres/outputs.tf
Normal file
3
sample/postgres/outputs.tf
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
output "postgres_flavor" {
|
||||||
|
value = data.stackitprivatepreview_postgresflexalpha_flavor.pgsql_flavor.flavor_id
|
||||||
|
}
|
||||||
79
sample/postgres/postresql.tf
Normal file
79
sample/postgres/postresql.tf
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
data "stackitprivatepreview_postgresflexalpha_flavor" "pgsql_flavor" {
|
||||||
|
project_id = var.project_id
|
||||||
|
region = "eu01"
|
||||||
|
cpu = 2
|
||||||
|
ram = 4
|
||||||
|
node_type = "Single"
|
||||||
|
storage_class = "premium-perf2-stackit"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "stackitprivatepreview_postgresflexalpha_instance" "msh-sna-pe-example" {
|
||||||
|
project_id = var.project_id
|
||||||
|
name = "msh-sna-pe-example"
|
||||||
|
backup_schedule = "0 0 * * *"
|
||||||
|
retention_days = 33
|
||||||
|
flavor_id = data.stackitprivatepreview_postgresflexalpha_flavor.pgsql_flavor.flavor_id
|
||||||
|
replicas = 1
|
||||||
|
storage = {
|
||||||
|
# class = "premium-perf2-stackit"
|
||||||
|
class = data.stackitprivatepreview_postgresflexalpha_flavor.pgsql_flavor.storage_class
|
||||||
|
size = 10
|
||||||
|
}
|
||||||
|
encryption = {
|
||||||
|
# key_id = stackit_kms_key.key.key_id
|
||||||
|
# keyring_id = stackit_kms_keyring.keyring.keyring_id
|
||||||
|
key_id = var.key_id
|
||||||
|
keyring_id = var.keyring_id
|
||||||
|
key_version = var.key_version
|
||||||
|
service_account = var.sa_email
|
||||||
|
}
|
||||||
|
network = {
|
||||||
|
acl = ["0.0.0.0/0", "193.148.160.0/19", "170.85.2.177/32"]
|
||||||
|
access_scope = "SNA"
|
||||||
|
}
|
||||||
|
version = 14
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser" {
|
||||||
|
project_id = var.project_id
|
||||||
|
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
|
||||||
|
username = var.db_admin_username
|
||||||
|
roles = ["createdb", "login"]
|
||||||
|
# roles = ["createdb", "login", "createrole"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
|
||||||
|
project_id = var.project_id
|
||||||
|
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
|
||||||
|
username = var.db_username
|
||||||
|
roles = ["login"]
|
||||||
|
# roles = ["createdb", "login", "createrole"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "stackitprivatepreview_postgresflexalpha_database" "example" {
|
||||||
|
depends_on = [stackitprivatepreview_postgresflexalpha_user.ptlsdbadminuser]
|
||||||
|
project_id = var.project_id
|
||||||
|
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
|
||||||
|
name = var.db_name
|
||||||
|
owner = var.db_admin_username
|
||||||
|
}
|
||||||
|
|
||||||
|
data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" {
|
||||||
|
project_id = var.project_id
|
||||||
|
instance_id = var.instance_id
|
||||||
|
region = "eu01"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "psql_instance_id" {
|
||||||
|
value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql.instance_id
|
||||||
|
}
|
||||||
|
|
||||||
|
output "psql_user_password" {
|
||||||
|
value = stackitprivatepreview_postgresflexalpha_user.ptlsdbuser.password
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "psql_user_conn" {
|
||||||
|
value = stackitprivatepreview_postgresflexalpha_user.ptlsdbuser.connection_string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -20,5 +20,5 @@ terraform {
|
||||||
provider "stackitprivatepreview" {
|
provider "stackitprivatepreview" {
|
||||||
default_region = "eu01"
|
default_region = "eu01"
|
||||||
enable_beta_resources = true
|
enable_beta_resources = true
|
||||||
service_account_key_path = "./service_account.json"
|
service_account_key_path = "../service_account.json"
|
||||||
}
|
}
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
resource "stackitprivatepreview_postgresflexalpha_instance" "ptlsdbsrv" {
|
|
||||||
project_id = var.project_id
|
|
||||||
name = "pgsql-example-instance"
|
|
||||||
backup_schedule = "0 0 * * *"
|
|
||||||
retention_days = 33
|
|
||||||
flavor = {
|
|
||||||
cpu = 2
|
|
||||||
ram = 4
|
|
||||||
}
|
|
||||||
replicas = 1
|
|
||||||
storage = {
|
|
||||||
class = "premium-perf2-stackit"
|
|
||||||
size = 5
|
|
||||||
}
|
|
||||||
encryption = {
|
|
||||||
# key_id = stackit_kms_key.key.key_id
|
|
||||||
# keyring_id = stackit_kms_keyring.keyring.keyring_id
|
|
||||||
key_id = var.key_id
|
|
||||||
keyring_id = var.keyring_id
|
|
||||||
key_version = var.key_version
|
|
||||||
service_account = var.sa_email
|
|
||||||
}
|
|
||||||
network = {
|
|
||||||
acl = ["0.0.0.0/0", "193.148.160.0/19"]
|
|
||||||
access_scope = "SNA"
|
|
||||||
}
|
|
||||||
version = 14
|
|
||||||
}
|
|
||||||
|
|
||||||
# data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" {
|
|
||||||
# project_id = var.project_id
|
|
||||||
# instance_id = "fdb6573e-2dea-4e1d-a638-9157cf90c3ba"
|
|
||||||
# region = "eu01"
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# output "sample_psqlinstance" {
|
|
||||||
# value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql
|
|
||||||
# }
|
|
||||||
12
sample/sqlserver/flavor.tf
Normal file
12
sample/sqlserver/flavor.tf
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
data "stackitprivatepreview_sqlserverflexalpha_flavor" "sqlserver_flavor" {
|
||||||
|
project_id = var.project_id
|
||||||
|
region = "eu01"
|
||||||
|
cpu = 4
|
||||||
|
ram = 16
|
||||||
|
node_type = "Single"
|
||||||
|
storage_class = "premium-perf2-stackit"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "sqlserver_flavor" {
|
||||||
|
value = data.stackitprivatepreview_sqlserverflexalpha_flavor.sqlserver_flavor.flavor_id
|
||||||
|
}
|
||||||
24
sample/sqlserver/providers.tf
Normal file
24
sample/sqlserver/providers.tf
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
# stackit = {
|
||||||
|
# source = "registry.terraform.io/stackitcloud/stackit"
|
||||||
|
# version = "~> 0.70"
|
||||||
|
# }
|
||||||
|
stackitprivatepreview = {
|
||||||
|
source = "registry.terraform.io/mhenselin/stackitprivatepreview"
|
||||||
|
version = "~> 0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# provider "stackit" {
|
||||||
|
# default_region = "eu01"
|
||||||
|
# enable_beta_resources = true
|
||||||
|
# service_account_key_path = "./service_account.json"
|
||||||
|
# }
|
||||||
|
|
||||||
|
provider "stackitprivatepreview" {
|
||||||
|
default_region = "eu01"
|
||||||
|
enable_beta_resources = true
|
||||||
|
service_account_key_path = "../service_account.json"
|
||||||
|
}
|
||||||
|
|
@ -18,42 +18,12 @@
|
||||||
# value = stackit_kms_key.key.key_id
|
# value = stackit_kms_key.key.key_id
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# resource "stackitalpha_postgresflexalpha_instance" "ptlsdbsrv" {
|
resource "stackitprivatepreview_sqlserverflexalpha_instance" "sqlsrv" {
|
||||||
# project_id = var.project_id
|
|
||||||
# name = "example-instance"
|
|
||||||
# acl = ["0.0.0.0/0"]
|
|
||||||
# backup_schedule = "0 0 * * *"
|
|
||||||
# flavor = {
|
|
||||||
# cpu = 2
|
|
||||||
# ram = 4
|
|
||||||
# }
|
|
||||||
# replicas = 1
|
|
||||||
# storage = {
|
|
||||||
# class = "premium-perf2-stackit"
|
|
||||||
# size = 5
|
|
||||||
# }
|
|
||||||
# version = 14
|
|
||||||
# encryption = {
|
|
||||||
# key_id = stackitalpha_kms_key.key.id
|
|
||||||
# keyring_id = stackitalpha_kms_keyring.keyring.keyring_id
|
|
||||||
# key_version = "1"
|
|
||||||
# service_account = var.sa_email
|
|
||||||
# }
|
|
||||||
# network = {
|
|
||||||
# access_scope = "SNA"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
resource "stackitprivatepreview_sqlserverflexalpha_instance" "ptlsdbsqlsrv" {
|
|
||||||
project_id = var.project_id
|
project_id = var.project_id
|
||||||
name = "msh-example-instance-002"
|
name = "msh-example-instance-002"
|
||||||
backup_schedule = "0 3 * * *"
|
backup_schedule = "0 3 * * *"
|
||||||
retention_days = 31
|
retention_days = 31
|
||||||
flavor = {
|
flavor_id = data.stackitprivatepreview_sqlserverflexalpha_flavor.sqlserver_flavor.flavor_id
|
||||||
cpu = 4
|
|
||||||
ram = 16
|
|
||||||
node_type = "Single"
|
|
||||||
}
|
|
||||||
storage = {
|
storage = {
|
||||||
class = "premium-perf2-stackit"
|
class = "premium-perf2-stackit"
|
||||||
size = 50
|
size = 50
|
||||||
|
|
@ -73,28 +43,27 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "ptlsdbsqlsrv" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# data "stackitalpha_sqlserverflexalpha_instance" "test" {
|
# data "stackitprivatepreview_sqlserverflexalpha_instance" "test" {
|
||||||
# project_id = var.project_id
|
# project_id = var.project_id
|
||||||
# instance_id = var.instance_id
|
# instance_id = var.instance_id
|
||||||
# region = "eu01"
|
# region = "eu01"
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# output "test" {
|
# output "test" {
|
||||||
# value = data.stackitalpha_sqlserverflexalpha_instance.test
|
# value = data.stackitprivatepreview_sqlserverflexalpha_instance.test
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# data "stackitalpha_sqlserverflexalpha_user" "testuser" {
|
resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbadminuser" {
|
||||||
# project_id = var.project_id
|
project_id = var.project_id
|
||||||
# instance_id = var.instance_id
|
instance_id = stackitprivatepreview_sqlserverflexalpha_instance.sqlsrv.instance_id
|
||||||
# region = "eu01"
|
username = var.db_admin_username
|
||||||
# }
|
roles = ["##STACKIT_LoginManager##", "##STACKIT_DatabaseManager##"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" {
|
||||||
|
project_id = var.project_id
|
||||||
|
instance_id = stackitprivatepreview_sqlserverflexalpha_instance.sqlsrv.instance_id
|
||||||
|
username = var.db_username
|
||||||
|
roles = ["##STACKIT_LoginManager##"]
|
||||||
|
}
|
||||||
|
|
||||||
# data "stackitprivatepreview_sqlserverflexalpha_instance" "existing" {
|
|
||||||
# project_id = var.project_id
|
|
||||||
# instance_id = "b31575e9-9dbd-4ff6-b341-82d89c34f14f"
|
|
||||||
# region = "eu01"
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# output "myinstance" {
|
|
||||||
# value = data.stackitprivatepreview_sqlserverflexalpha_instance.existing
|
|
||||||
# }
|
|
||||||
11
sample/sqlserver/variables.tf.example
Normal file
11
sample/sqlserver/variables.tf.example
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
variable "project_id" {
|
||||||
|
default = "<PROJECT ID UUID>"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "sa_email" {
|
||||||
|
default = "<SERVICE ACCOUNT EMAIL>"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_username" {
|
||||||
|
default = "<DB USERNAME>"
|
||||||
|
}
|
||||||
58
sample/tf.sh
58
sample/tf.sh
|
|
@ -3,28 +3,44 @@
|
||||||
|
|
||||||
# ./tf.sh apply > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
|
# ./tf.sh apply > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
|
||||||
|
|
||||||
# copy or rename sample.tfrc.example and adjust it
|
usage() {
|
||||||
TERRAFORM_CONFIG=$(pwd)/sample.tfrc
|
echo "$0 usage:" && grep "[[:space:]].)\ #" "$0" | sed 's/#//' | sed -r 's/([a-z])\)/-\1/';
|
||||||
export TERRAFORM_CONFIG
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
parsed_options=$(
|
[ $# -eq 0 ] && usage
|
||||||
getopt -n "$0" -o dil -- "$@"
|
|
||||||
) || exit
|
CONFIG_FOLDER=$(dirname "$0")
|
||||||
eval "set -- $parsed_options"
|
BINARY=terraform
|
||||||
while [ "$#" -gt 0 ]; do
|
|
||||||
case $1 in
|
while getopts ":b:hdit" arg; do
|
||||||
(-d) TF_LOG=DEBUG
|
case $arg in
|
||||||
export TF_LOG
|
b) # Set binary (default is terraform).
|
||||||
shift;;
|
BINARY=${OPTARG}
|
||||||
(-i) TF_LOG=INFO
|
shift 2
|
||||||
export TF_LOG
|
;;
|
||||||
shift;;
|
d) # Set log level to DEBUG.
|
||||||
(-t) TF_LOG=TRACE
|
TF_LOG=DEBUG
|
||||||
export TF_LOG
|
export TF_LOG
|
||||||
shift;;
|
shift
|
||||||
(--) shift; break;;
|
;;
|
||||||
(*) echo "Unknown option ${1}" # should never be reached.
|
i) # Set log level to INFO.
|
||||||
|
TF_LOG=INFO
|
||||||
|
export TF_LOG
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
t) # Set log level to TRACE.
|
||||||
|
TF_LOG=TRACE
|
||||||
|
export TF_LOG
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
h | *) # Display help.
|
||||||
|
usage
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
terraform "$*"
|
TERRAFORM_CONFIG=${CONFIG_FOLDER}/config.tfrc
|
||||||
|
export TERRAFORM_CONFIG
|
||||||
|
|
||||||
|
${BINARY} "$@"
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
|
|
||||||
# ./tofu.sh apply > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
|
|
||||||
|
|
||||||
# copy or rename sample.tfrc.example and adjust it
|
|
||||||
TERRAFORM_CONFIG=$(pwd)/sample.tfrc
|
|
||||||
export TERRAFORM_CONFIG
|
|
||||||
|
|
||||||
parsed_options=$(
|
|
||||||
getopt -n "$0" -o l -- "$@"
|
|
||||||
) || exit
|
|
||||||
eval "set -- $parsed_options"
|
|
||||||
while [ "$#" -gt 0 ]; do
|
|
||||||
case $1 in
|
|
||||||
(-l) TF_LOG=TRACE
|
|
||||||
export TF_LOG
|
|
||||||
shift;;
|
|
||||||
(--) shift; break;;
|
|
||||||
(*) echo "Unknown option ${1}" # should never be reached.
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
tofu "$*"
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# data "stackitprivatepreview_postgresflexalpha_user" "example" {
|
|
||||||
# project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id
|
|
||||||
# instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.instance_id
|
|
||||||
# user_id = 1
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
|
|
||||||
# project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id
|
|
||||||
# instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.instance_id
|
|
||||||
# username = var.db_username
|
|
||||||
# roles = ["createdb", "login"]
|
|
||||||
# # roles = ["createdb", "login", "createrole"]
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" {
|
|
||||||
# project_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.project_id
|
|
||||||
# instance_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.instance_id
|
|
||||||
# username = var.db_username
|
|
||||||
# roles = ["login"]
|
|
||||||
# }
|
|
||||||
|
|
@ -9,7 +9,7 @@ ROOT_DIR=$(git rev-parse --show-toplevel)
|
||||||
before_hash=$(find docs -type f -exec sha256sum {} \; | sort | sha256sum | awk '{print $1}')
|
before_hash=$(find docs -type f -exec sha256sum {} \; | sort | sha256sum | awk '{print $1}')
|
||||||
|
|
||||||
# re-generate the docs
|
# re-generate the docs
|
||||||
$ROOT_DIR/scripts/tfplugindocs.sh
|
"${ROOT_DIR}/scripts/tfplugindocs.sh"
|
||||||
|
|
||||||
after_hash=$(find docs -type f -exec sha256sum {} \; | sort | sha256sum | awk '{print $1}')
|
after_hash=$(find docs -type f -exec sha256sum {} \; | sort | sha256sum | awk '{print $1}')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"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/int64planmodifier"
|
||||||
"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"
|
||||||
|
|
@ -90,7 +93,7 @@ func (r *databaseResource) ModifyPlan(
|
||||||
|
|
||||||
// Metadata returns the resource type name.
|
// Metadata returns the resource type name.
|
||||||
func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||||
resp.TypeName = req.ProviderTypeName + "_postgresflex_database"
|
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_database"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure adds the provider configured client to the resource.
|
// Configure adds the provider configured client to the resource.
|
||||||
|
|
@ -136,15 +139,13 @@ func (r *databaseResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"database_id": schema.StringAttribute{
|
"database_id": schema.Int64Attribute{
|
||||||
Description: descriptions["database_id"],
|
Description: descriptions["database_id"],
|
||||||
Computed: true,
|
Computed: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.Int64{
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
int64planmodifier.UseStateForUnknown(),
|
||||||
},
|
|
||||||
Validators: []validator.String{
|
|
||||||
validate.NoSeparator(),
|
|
||||||
},
|
},
|
||||||
|
Validators: []validator.Int64{},
|
||||||
},
|
},
|
||||||
"instance_id": schema.StringAttribute{
|
"instance_id": schema.StringAttribute{
|
||||||
Description: descriptions["instance_id"],
|
Description: descriptions["instance_id"],
|
||||||
|
|
@ -171,18 +172,20 @@ func (r *databaseResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"name": schema.StringAttribute{
|
"name": schema.StringAttribute{
|
||||||
Description: descriptions["name"],
|
Description: descriptions["name"],
|
||||||
Required: true,
|
Required: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{},
|
||||||
stringplanmodifier.RequiresReplace(),
|
Validators: []validator.String{
|
||||||
|
stringvalidator.RegexMatches(
|
||||||
|
regexp.MustCompile("^[a-z]([a-z0-9]*)?$"),
|
||||||
|
"must start with a letter, must have lower case letters or numbers",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"owner": schema.StringAttribute{
|
"owner": schema.StringAttribute{
|
||||||
Description: descriptions["owner"],
|
Description: descriptions["owner"],
|
||||||
Required: true,
|
Required: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{},
|
||||||
stringplanmodifier.RequiresReplace(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"region": schema.StringAttribute{
|
"region": schema.StringAttribute{
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
|
@ -348,11 +351,86 @@ func (r *databaseResource) 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 *databaseResource) Update(
|
func (r *databaseResource) Update(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
_ resource.UpdateRequest,
|
req resource.UpdateRequest,
|
||||||
resp *resource.UpdateResponse,
|
resp *resource.UpdateResponse,
|
||||||
) { // nolint:gocritic // function signature required by Terraform
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Update shouldn't be called
|
var model Model
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating database", "Database can't be updated")
|
diags := req.Plan.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
instanceId := model.InstanceId.ValueString()
|
||||||
|
databaseId := model.DatabaseId.ValueInt64()
|
||||||
|
region := model.Region.ValueString()
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
// Retrieve values from state
|
||||||
|
var stateModel Model
|
||||||
|
diags = req.State.Get(ctx, &stateModel)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
modified := false
|
||||||
|
var payload postgresflexalpha.UpdateDatabasePartiallyRequestPayload
|
||||||
|
if stateModel.Name != model.Name {
|
||||||
|
payload.Name = model.Name.ValueStringPointer()
|
||||||
|
modified = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if stateModel.Owner != model.Owner {
|
||||||
|
payload.Owner = model.Owner.ValueStringPointer()
|
||||||
|
modified = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !modified {
|
||||||
|
tflog.Info(ctx, "no modification detected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing database
|
||||||
|
res, err := r.client.UpdateDatabasePartiallyRequest(
|
||||||
|
ctx,
|
||||||
|
projectId,
|
||||||
|
region,
|
||||||
|
instanceId,
|
||||||
|
databaseId,
|
||||||
|
).UpdateDatabasePartiallyRequestPayload(payload).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "error updating database", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
// Map response body to schema
|
||||||
|
err = mapFieldsUpdatePartially(res, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error updating database",
|
||||||
|
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, "Postgres Flex database updated")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the resource and removes the Terraform state on success.
|
// Delete deletes the resource and removes the Terraform state on success.
|
||||||
|
|
@ -361,7 +439,6 @@ func (r *databaseResource) Delete(
|
||||||
req resource.DeleteRequest,
|
req resource.DeleteRequest,
|
||||||
resp *resource.DeleteResponse,
|
resp *resource.DeleteResponse,
|
||||||
) { // nolint:gocritic // function signature required by Terraform
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.State.Get(ctx, &model)
|
diags := req.State.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -424,11 +501,11 @@ func (r *databaseResource) ImportState(
|
||||||
tflog.Info(ctx, "Postgres Flex database state imported")
|
tflog.Info(ctx, "Postgres Flex database state imported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapFields(databaseResp *postgresflexalpha.ListDatabase, model *Model, region string) error {
|
func mapFields(resp *postgresflexalpha.ListDatabase, model *Model, region string) error {
|
||||||
if databaseResp == nil {
|
if resp == nil {
|
||||||
return fmt.Errorf("response is nil")
|
return fmt.Errorf("response is nil")
|
||||||
}
|
}
|
||||||
if databaseResp.Id == nil || *databaseResp.Id == 0 {
|
if resp.Id == nil || *resp.Id == 0 {
|
||||||
return fmt.Errorf("id not present")
|
return fmt.Errorf("id not present")
|
||||||
}
|
}
|
||||||
if model == nil {
|
if model == nil {
|
||||||
|
|
@ -438,8 +515,8 @@ func mapFields(databaseResp *postgresflexalpha.ListDatabase, model *Model, regio
|
||||||
var databaseId int64
|
var databaseId int64
|
||||||
if model.DatabaseId.ValueInt64() != 0 {
|
if model.DatabaseId.ValueInt64() != 0 {
|
||||||
databaseId = model.DatabaseId.ValueInt64()
|
databaseId = model.DatabaseId.ValueInt64()
|
||||||
} else if databaseResp.Id != nil {
|
} else if resp.Id != nil {
|
||||||
databaseId = *databaseResp.Id
|
databaseId = *resp.Id
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("database id not present")
|
return fmt.Errorf("database id not present")
|
||||||
}
|
}
|
||||||
|
|
@ -447,33 +524,32 @@ func mapFields(databaseResp *postgresflexalpha.ListDatabase, model *Model, regio
|
||||||
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(databaseId, 10),
|
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(databaseId, 10),
|
||||||
)
|
)
|
||||||
model.DatabaseId = types.Int64Value(databaseId)
|
model.DatabaseId = types.Int64Value(databaseId)
|
||||||
model.Name = types.StringPointerValue(databaseResp.Name)
|
model.Name = types.StringPointerValue(resp.Name)
|
||||||
model.Region = types.StringValue(region)
|
model.Region = types.StringValue(region)
|
||||||
|
model.Owner = types.StringPointerValue(cleanString(resp.Owner))
|
||||||
ownerStr, err := mapOwner(databaseResp)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error mapping owner: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Owner = types.StringPointerValue(ownerStr)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapOwner(databaseResp *postgresflexalpha.ListDatabase) (*string, error) {
|
func mapFieldsUpdate(res *postgresflexalpha.UpdateDatabaseResponse, model *Model, region string) error {
|
||||||
if databaseResp == nil {
|
if res == nil {
|
||||||
return nil, fmt.Errorf("response is nil")
|
return fmt.Errorf("response is nil")
|
||||||
}
|
}
|
||||||
|
return mapFields(res.Database, model, region)
|
||||||
|
}
|
||||||
|
|
||||||
if databaseResp.Owner == nil {
|
func mapFieldsUpdatePartially(res *postgresflexalpha.UpdateDatabasePartiallyResponse, model *Model, region string) error {
|
||||||
return nil, nil
|
if res == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
}
|
}
|
||||||
ownerStr := *databaseResp.Owner
|
return mapFields(res.Database, model, region)
|
||||||
|
}
|
||||||
|
|
||||||
// If the field is returned between with quotes, we trim them to prevent an inconsistent result after apply
|
func cleanString(s *string) *string {
|
||||||
ownerStr = strings.TrimPrefix(ownerStr, `"`)
|
if s == nil {
|
||||||
ownerStr = strings.TrimSuffix(ownerStr, `"`)
|
return nil
|
||||||
|
}
|
||||||
return &ownerStr, nil
|
res := strings.Trim(*s, "\"")
|
||||||
|
return &res
|
||||||
}
|
}
|
||||||
|
|
||||||
func toCreatePayload(model *Model) (*postgresflexalpha.CreateDatabaseRequestPayload, error) {
|
func toCreatePayload(model *Model) (*postgresflexalpha.CreateDatabaseRequestPayload, error) {
|
||||||
|
|
@ -496,6 +572,7 @@ func getDatabase(
|
||||||
projectId, region, instanceId string,
|
projectId, region, instanceId string,
|
||||||
databaseId int64,
|
databaseId int64,
|
||||||
) (*postgresflexalpha.ListDatabase, error) {
|
) (*postgresflexalpha.ListDatabase, error) {
|
||||||
|
// TODO - implement pagination handling
|
||||||
resp, err := client.ListDatabasesRequestExecute(ctx, projectId, region, instanceId)
|
resp, err := client.ListDatabasesRequestExecute(ctx, projectId, region, instanceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package postgresflexalpha
|
package postgresflexalpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
@ -182,3 +183,50 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_cleanString(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s *string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple_value",
|
||||||
|
args: args{
|
||||||
|
s: utils.Ptr("mytest"),
|
||||||
|
},
|
||||||
|
want: utils.Ptr("mytest"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple_value_with_quotes",
|
||||||
|
args: args{
|
||||||
|
s: utils.Ptr("\"mytest\""),
|
||||||
|
},
|
||||||
|
want: utils.Ptr("mytest"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple_values_with_quotes",
|
||||||
|
args: args{
|
||||||
|
s: utils.Ptr("\"my test here\""),
|
||||||
|
},
|
||||||
|
want: utils.Ptr("my test here"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple_values",
|
||||||
|
args: args{
|
||||||
|
s: utils.Ptr("my test here"),
|
||||||
|
},
|
||||||
|
want: utils.Ptr("my test here"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := cleanString(tt.args.s); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("cleanString() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,502 +0,0 @@
|
||||||
package postgresflex
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ basetypes.ObjectTypable = FlavorType{}
|
|
||||||
|
|
||||||
type FlavorType struct {
|
|
||||||
basetypes.ObjectType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t FlavorType) Equal(o attr.Type) bool {
|
|
||||||
other, ok := o.(FlavorType)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.ObjectType.Equal(other.ObjectType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t FlavorType) String() string {
|
|
||||||
return "FlavorType"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t FlavorType) ValueFromObject(_ context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
|
||||||
var diags diag.Diagnostics
|
|
||||||
|
|
||||||
attributes := in.Attributes()
|
|
||||||
|
|
||||||
cpuAttribute, ok := attributes["cpu"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`cpu is missing from object`)
|
|
||||||
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuVal, ok := cpuAttribute.(basetypes.Int64Value)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`cpu expected to be basetypes.Int64Value, was: %T`, cpuAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionAttribute, ok := attributes["description"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`description is missing from object`)
|
|
||||||
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
idAttribute, ok := attributes["id"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`id is missing from object`)
|
|
||||||
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
idVal, ok := idAttribute.(basetypes.StringValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`id expected to be basetypes.StringValue, was: %T`, idAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryAttribute, ok := attributes["memory"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`memory is missing from object`)
|
|
||||||
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
ramVal, ok := memoryAttribute.(basetypes.Int64Value)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`memory expected to be basetypes.Int64Value, was: %T`, memoryAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
if diags.HasError() {
|
|
||||||
return nil, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlavorValue{
|
|
||||||
Cpu: cpuVal,
|
|
||||||
Description: descriptionVal,
|
|
||||||
Id: idVal,
|
|
||||||
Ram: ramVal,
|
|
||||||
state: attr.ValueStateKnown,
|
|
||||||
}, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFlavorValueNull() FlavorValue {
|
|
||||||
return FlavorValue{
|
|
||||||
state: attr.ValueStateNull,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFlavorValueUnknown() FlavorValue {
|
|
||||||
return FlavorValue{
|
|
||||||
state: attr.ValueStateUnknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFlavorValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (FlavorValue, diag.Diagnostics) {
|
|
||||||
var diags diag.Diagnostics
|
|
||||||
|
|
||||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
for name, attributeType := range attributeTypes {
|
|
||||||
attribute, ok := attributes[name]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Missing FlavorValue Attribute Value",
|
|
||||||
"While creating a FlavorValue value, a missing attribute value was detected. "+
|
|
||||||
"A FlavorValue must contain values for all attributes, even if null or unknown. "+
|
|
||||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
|
||||||
fmt.Sprintf("FlavorValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
|
||||||
diags.AddError(
|
|
||||||
"Invalid FlavorValue Attribute Type",
|
|
||||||
"While creating a FlavorValue value, an invalid attribute value was detected. "+
|
|
||||||
"A FlavorValue must use a matching attribute type for the value. "+
|
|
||||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
|
||||||
fmt.Sprintf("FlavorValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
|
||||||
fmt.Sprintf("FlavorValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for name := range attributes {
|
|
||||||
_, ok := attributeTypes[name]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Extra FlavorValue Attribute Value",
|
|
||||||
"While creating a FlavorValue value, an extra attribute value was detected. "+
|
|
||||||
"A FlavorValue must not contain values beyond the expected attribute types. "+
|
|
||||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
|
||||||
fmt.Sprintf("Extra FlavorValue Attribute Name: %s", name),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if diags.HasError() {
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuAttribute, ok := attributes["cpu"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`cpu is missing from object`)
|
|
||||||
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuVal, ok := cpuAttribute.(basetypes.Int64Value)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`cpu expected to be basetypes.Int64Value, was: %T`, cpuAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionAttribute, ok := attributes["description"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`description is missing from object`)
|
|
||||||
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
idAttribute, ok := attributes["id"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`id is missing from object`)
|
|
||||||
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
idVal, ok := idAttribute.(basetypes.StringValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`id expected to be basetypes.StringValue, was: %T`, idAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryAttribute, ok := attributes["memory"]
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Missing",
|
|
||||||
`memory is missing from object`)
|
|
||||||
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryVal, ok := memoryAttribute.(basetypes.Int64Value)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
diags.AddError(
|
|
||||||
"Attribute Wrong Type",
|
|
||||||
fmt.Sprintf(`memory expected to be basetypes.Int64Value, was: %T`, memoryAttribute))
|
|
||||||
}
|
|
||||||
|
|
||||||
if diags.HasError() {
|
|
||||||
return NewFlavorValueUnknown(), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlavorValue{
|
|
||||||
Cpu: cpuVal,
|
|
||||||
Description: descriptionVal,
|
|
||||||
Id: idVal,
|
|
||||||
Ram: memoryVal,
|
|
||||||
state: attr.ValueStateKnown,
|
|
||||||
}, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFlavorValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) FlavorValue {
|
|
||||||
object, diags := NewFlavorValue(attributeTypes, attributes)
|
|
||||||
|
|
||||||
if diags.HasError() {
|
|
||||||
// This could potentially be added to the diag package.
|
|
||||||
diagsStrings := make([]string, 0, len(diags))
|
|
||||||
|
|
||||||
for _, diagnostic := range diags {
|
|
||||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
|
||||||
"%s | %s | %s",
|
|
||||||
diagnostic.Severity(),
|
|
||||||
diagnostic.Summary(),
|
|
||||||
diagnostic.Detail()))
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("NewFlavorValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return object
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t FlavorType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
|
||||||
if in.Type() == nil {
|
|
||||||
return NewFlavorValueNull(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
|
||||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !in.IsKnown() {
|
|
||||||
return NewFlavorValueUnknown(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.IsNull() {
|
|
||||||
return NewFlavorValueNull(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes := map[string]attr.Value{}
|
|
||||||
|
|
||||||
val := map[string]tftypes.Value{}
|
|
||||||
|
|
||||||
err := in.As(&val)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range val {
|
|
||||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes[k] = a
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewFlavorValueMust(FlavorValue{}.AttributeTypes(ctx), attributes), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t FlavorType) ValueType(_ context.Context) attr.Value {
|
|
||||||
return FlavorValue{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ basetypes.ObjectValuable = FlavorValue{}
|
|
||||||
|
|
||||||
type FlavorValue struct {
|
|
||||||
Cpu basetypes.Int64Value `tfsdk:"cpu"`
|
|
||||||
Description basetypes.StringValue `tfsdk:"description"`
|
|
||||||
Id basetypes.StringValue `tfsdk:"id"`
|
|
||||||
Ram basetypes.Int64Value `tfsdk:"ram"`
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
|
||||||
attrTypes := make(map[string]tftypes.Type, 4)
|
|
||||||
|
|
||||||
var val tftypes.Value
|
|
||||||
var err error
|
|
||||||
|
|
||||||
attrTypes["cpu"] = basetypes.Int64Type{}.TerraformType(ctx)
|
|
||||||
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
|
|
||||||
attrTypes["id"] = basetypes.StringType{}.TerraformType(ctx)
|
|
||||||
attrTypes["memory"] = basetypes.Int64Type{}.TerraformType(ctx)
|
|
||||||
|
|
||||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
|
||||||
|
|
||||||
switch v.state {
|
|
||||||
case attr.ValueStateKnown:
|
|
||||||
vals := make(map[string]tftypes.Value, 4)
|
|
||||||
|
|
||||||
val, err = v.Cpu.ToTerraformValue(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
|
||||||
}
|
|
||||||
|
|
||||||
vals["cpu"] = val
|
|
||||||
|
|
||||||
val, err = v.Description.ToTerraformValue(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
|
||||||
}
|
|
||||||
|
|
||||||
vals["description"] = val
|
|
||||||
|
|
||||||
val, err = v.Id.ToTerraformValue(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
|
||||||
}
|
|
||||||
|
|
||||||
vals["id"] = val
|
|
||||||
|
|
||||||
val, err = v.Ram.ToTerraformValue(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
|
||||||
}
|
|
||||||
|
|
||||||
vals["memory"] = val
|
|
||||||
|
|
||||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tftypes.NewValue(objectType, vals), nil
|
|
||||||
case attr.ValueStateNull:
|
|
||||||
return tftypes.NewValue(objectType, nil), nil
|
|
||||||
case attr.ValueStateUnknown:
|
|
||||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) IsNull() bool {
|
|
||||||
return v.state == attr.ValueStateNull
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) IsUnknown() bool {
|
|
||||||
return v.state == attr.ValueStateUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) String() string {
|
|
||||||
return "FlavorValue"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) ToObjectValue(_ context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
|
||||||
var diags diag.Diagnostics
|
|
||||||
|
|
||||||
attributeTypes := map[string]attr.Type{
|
|
||||||
"cpu": basetypes.Int64Type{},
|
|
||||||
"description": basetypes.StringType{},
|
|
||||||
"id": basetypes.StringType{},
|
|
||||||
"memory": basetypes.Int64Type{},
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.IsNull() {
|
|
||||||
return types.ObjectNull(attributeTypes), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.IsUnknown() {
|
|
||||||
return types.ObjectUnknown(attributeTypes), diags
|
|
||||||
}
|
|
||||||
|
|
||||||
objVal, diags := types.ObjectValue(
|
|
||||||
attributeTypes,
|
|
||||||
map[string]attr.Value{
|
|
||||||
"cpu": v.Cpu,
|
|
||||||
"description": v.Description,
|
|
||||||
"id": v.Id,
|
|
||||||
"memory": v.Ram,
|
|
||||||
})
|
|
||||||
|
|
||||||
return objVal, diags
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) Equal(o attr.Value) bool {
|
|
||||||
other, ok := o.(FlavorValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.state != other.state {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.state != attr.ValueStateKnown {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Cpu.Equal(other.Cpu) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Description.Equal(other.Description) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Id.Equal(other.Id) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.Ram.Equal(other.Ram) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) Type(ctx context.Context) attr.Type {
|
|
||||||
return FlavorType{
|
|
||||||
basetypes.ObjectType{
|
|
||||||
AttrTypes: v.AttributeTypes(ctx),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v FlavorValue) AttributeTypes(_ context.Context) map[string]attr.Type {
|
|
||||||
return map[string]attr.Type{
|
|
||||||
"cpu": basetypes.Int64Type{},
|
|
||||||
"description": basetypes.StringType{},
|
|
||||||
"id": basetypes.StringType{},
|
|
||||||
"memory": basetypes.Int64Type{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
251
stackit/internal/services/postgresflexalpha/flavor/datasource.go
Normal file
251
stackit/internal/services/postgresflexalpha/flavor/datasource.go
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
package postgresFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
|
||||||
|
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
var (
|
||||||
|
_ datasource.DataSource = &flavorDataSource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlavorModel struct {
|
||||||
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
|
Region types.String `tfsdk:"region"`
|
||||||
|
StorageClass types.String `tfsdk:"storage_class"`
|
||||||
|
Cpu types.Int64 `tfsdk:"cpu"`
|
||||||
|
Description types.String `tfsdk:"description"`
|
||||||
|
Id types.String `tfsdk:"id"`
|
||||||
|
FlavorId types.String `tfsdk:"flavor_id"`
|
||||||
|
MaxGb types.Int64 `tfsdk:"max_gb"`
|
||||||
|
Memory types.Int64 `tfsdk:"ram"`
|
||||||
|
MinGb types.Int64 `tfsdk:"min_gb"`
|
||||||
|
NodeType types.String `tfsdk:"node_type"`
|
||||||
|
StorageClasses types.List `tfsdk:"storage_classes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFlavorDataSource is a helper function to simplify the provider implementation.
|
||||||
|
func NewFlavorDataSource() datasource.DataSource {
|
||||||
|
return &flavorDataSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flavorDataSource is the data source implementation.
|
||||||
|
type flavorDataSource struct {
|
||||||
|
client *postgresflexalpha.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the data source type name.
|
||||||
|
func (r *flavorDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||||
|
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavor"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure adds the provider configured client to the data source.
|
||||||
|
func (r *flavorDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.client = apiClient
|
||||||
|
tflog.Info(ctx, "Postgres Flex instance client configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
|
resp.Schema = schema.Schema{
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"project_id": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The cpu count of the instance.",
|
||||||
|
MarkdownDescription: "The cpu count of the instance.",
|
||||||
|
},
|
||||||
|
"region": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The flavor description.",
|
||||||
|
MarkdownDescription: "The flavor description.",
|
||||||
|
},
|
||||||
|
"cpu": schema.Int64Attribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The cpu count of the instance.",
|
||||||
|
MarkdownDescription: "The cpu count of the instance.",
|
||||||
|
},
|
||||||
|
"ram": schema.Int64Attribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The memory of the instance in Gibibyte.",
|
||||||
|
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||||
|
},
|
||||||
|
"storage_class": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The memory of the instance in Gibibyte.",
|
||||||
|
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||||
|
},
|
||||||
|
"description": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The flavor description.",
|
||||||
|
MarkdownDescription: "The flavor description.",
|
||||||
|
},
|
||||||
|
"id": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The terraform id of the instance flavor.",
|
||||||
|
MarkdownDescription: "The terraform id of the instance flavor.",
|
||||||
|
},
|
||||||
|
"flavor_id": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The flavor id of the instance flavor.",
|
||||||
|
MarkdownDescription: "The flavor id of the instance flavor.",
|
||||||
|
},
|
||||||
|
"max_gb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||||
|
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||||
|
},
|
||||||
|
"min_gb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "minimum storage which is required to order in Gigabyte.",
|
||||||
|
MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
|
||||||
|
},
|
||||||
|
"node_type": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "defines the nodeType it can be either single or replica",
|
||||||
|
MarkdownDescription: "defines the nodeType it can be either single or replica",
|
||||||
|
},
|
||||||
|
"storage_classes": schema.ListNestedAttribute{
|
||||||
|
Computed: true,
|
||||||
|
NestedObject: schema.NestedAttributeObject{
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"class": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"max_io_per_sec": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"max_through_in_mb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomType: postgresflex.StorageClassesType{
|
||||||
|
ObjectType: types.ObjectType{
|
||||||
|
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
|
var model FlavorModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
flavors, err := getAllFlavors(ctx, r.client, projectId, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading flavors", fmt.Sprintf("getAllFlavors: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundFlavors []postgresflexalpha.ListFlavors
|
||||||
|
for _, flavor := range flavors {
|
||||||
|
if model.Cpu.ValueInt64() != *flavor.Cpu {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if model.Memory.ValueInt64() != *flavor.Memory {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if model.NodeType.ValueString() != *flavor.NodeType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, sc := range *flavor.StorageClasses {
|
||||||
|
if model.StorageClass.ValueString() != *sc.Class {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundFlavors = append(foundFlavors, flavor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(foundFlavors) == 0 {
|
||||||
|
resp.Diagnostics.AddError("get flavor", "could not find requested flavor")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(foundFlavors) > 1 {
|
||||||
|
resp.Diagnostics.AddError("get flavor", "found too many matching flavors")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f := foundFlavors[0]
|
||||||
|
model.Description = types.StringValue(*f.Description)
|
||||||
|
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, *f.Id)
|
||||||
|
model.FlavorId = types.StringValue(*f.Id)
|
||||||
|
model.MaxGb = types.Int64Value(*f.MaxGB)
|
||||||
|
model.MinGb = types.Int64Value(*f.MinGB)
|
||||||
|
|
||||||
|
if f.StorageClasses == nil {
|
||||||
|
model.StorageClasses = types.ListNull(postgresflex.StorageClassesType{
|
||||||
|
ObjectType: basetypes.ObjectType{
|
||||||
|
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
var scList []attr.Value
|
||||||
|
for _, sc := range *f.StorageClasses {
|
||||||
|
scList = append(
|
||||||
|
scList,
|
||||||
|
postgresflex.NewStorageClassesValueMust(
|
||||||
|
postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
map[string]attr.Value{
|
||||||
|
"class": types.StringValue(*sc.Class),
|
||||||
|
"max_io_per_sec": types.Int64Value(*sc.MaxIoPerSec),
|
||||||
|
"max_through_in_mb": types.Int64Value(*sc.MaxThroughInMb),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
storageClassesList := types.ListValueMust(
|
||||||
|
postgresflex.StorageClassesType{
|
||||||
|
ObjectType: basetypes.ObjectType{
|
||||||
|
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scList,
|
||||||
|
)
|
||||||
|
model.StorageClasses = storageClassesList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set refreshed state
|
||||||
|
diags = resp.State.Set(ctx, model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors read")
|
||||||
|
}
|
||||||
201
stackit/internal/services/postgresflexalpha/flavor/functions.go
Normal file
201
stackit/internal/services/postgresflexalpha/flavor/functions.go
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
package postgresFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flavorsClient interface {
|
||||||
|
GetFlavorsRequestExecute(
|
||||||
|
ctx context.Context,
|
||||||
|
projectId, region string,
|
||||||
|
page, size *int64,
|
||||||
|
sort *postgresflex.FlavorSort,
|
||||||
|
) (*postgresflex.GetFlavorsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
|
||||||
|
// if model == nil {
|
||||||
|
// return fmt.Errorf("nil model")
|
||||||
|
// }
|
||||||
|
// if flavor == nil {
|
||||||
|
// return fmt.Errorf("nil flavor")
|
||||||
|
// }
|
||||||
|
// cpu := flavor.CPU.ValueInt64()
|
||||||
|
// if cpu == 0 {
|
||||||
|
// return fmt.Errorf("nil CPU")
|
||||||
|
// }
|
||||||
|
// ram := flavor.RAM.ValueInt64()
|
||||||
|
// if ram == 0 {
|
||||||
|
// return fmt.Errorf("nil RAM")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nodeType := flavor.NodeType.ValueString()
|
||||||
|
// if nodeType == "" {
|
||||||
|
// if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
|
||||||
|
// return fmt.Errorf("nil NodeType")
|
||||||
|
// }
|
||||||
|
// switch model.Replicas.ValueInt64() {
|
||||||
|
// case 1:
|
||||||
|
// nodeType = "Single"
|
||||||
|
// case 3:
|
||||||
|
// nodeType = "Replica"
|
||||||
|
// default:
|
||||||
|
// return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// storageClass := conversion.StringValueToPointer(storage.Class)
|
||||||
|
// if storageClass == nil {
|
||||||
|
// return fmt.Errorf("nil StorageClass")
|
||||||
|
// }
|
||||||
|
// storageSize := conversion.Int64ValueToPointer(storage.Size)
|
||||||
|
// if storageSize == nil {
|
||||||
|
// return fmt.Errorf("nil StorageSize")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// projectId := model.ProjectId.ValueString()
|
||||||
|
// region := model.Region.ValueString()
|
||||||
|
//
|
||||||
|
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// avl := ""
|
||||||
|
// foundFlavorCount := 0
|
||||||
|
// var foundFlavors []string
|
||||||
|
// for _, f := range flavorList {
|
||||||
|
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if !strings.EqualFold(*f.NodeType, nodeType) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *f.Cpu == cpu && *f.Memory == ram {
|
||||||
|
// var useSc *postgresflex.FlavorStorageClassesStorageClass
|
||||||
|
// for _, sc := range *f.StorageClasses {
|
||||||
|
// if *sc.Class != *storageClass {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
|
||||||
|
// return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
|
||||||
|
// }
|
||||||
|
// useSc = &sc
|
||||||
|
// }
|
||||||
|
// if useSc == nil {
|
||||||
|
// return fmt.Errorf("no storage class found for %s", *storageClass)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// flavor.Id = types.StringValue(*f.Id)
|
||||||
|
// flavor.Description = types.StringValue(*f.Description)
|
||||||
|
// foundFlavors = append(foundFlavors, fmt.Sprintf("%s (%d/%d - %s)", *f.Id, *f.Cpu, *f.Memory, *f.NodeType))
|
||||||
|
// foundFlavorCount++
|
||||||
|
// }
|
||||||
|
// for _, cls := range *f.StorageClasses {
|
||||||
|
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if foundFlavorCount > 1 {
|
||||||
|
// return fmt.Errorf(
|
||||||
|
// "number of flavors returned: %d\nmultiple flavors found: %d flavors\n %s",
|
||||||
|
// len(flavorList),
|
||||||
|
// foundFlavorCount,
|
||||||
|
// strings.Join(foundFlavors, "\n "),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// if flavor.Id.ValueString() == "" {
|
||||||
|
// return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
||||||
|
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) ([]postgresflex.ListFlavors, error) {
|
||||||
|
if projectId == "" || region == "" {
|
||||||
|
return nil, fmt.Errorf("listing postgresflex flavors: projectId and region are required")
|
||||||
|
}
|
||||||
|
var flavorList []postgresflex.ListFlavors
|
||||||
|
|
||||||
|
page := int64(1)
|
||||||
|
size := int64(10)
|
||||||
|
sort := postgresflex.FLAVORSORT_INDEX_ASC
|
||||||
|
counter := 0
|
||||||
|
for {
|
||||||
|
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing postgresflex flavors: %w", err)
|
||||||
|
}
|
||||||
|
if res.Flavors == nil {
|
||||||
|
return nil, fmt.Errorf("finding flavors for project %s", projectId)
|
||||||
|
}
|
||||||
|
pagination := res.GetPagination()
|
||||||
|
flavors := res.GetFlavors()
|
||||||
|
flavorList = append(flavorList, flavors...)
|
||||||
|
|
||||||
|
if *pagination.TotalRows < int64(len(flavorList)) {
|
||||||
|
return nil, fmt.Errorf("total rows is smaller than current accumulated list - that should not happen")
|
||||||
|
}
|
||||||
|
if *pagination.TotalRows == int64(len(flavorList)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
|
||||||
|
if page > *pagination.TotalPages {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement a breakpoint
|
||||||
|
counter++
|
||||||
|
if counter > 1000 {
|
||||||
|
panic("too many pagination results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flavorList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
|
||||||
|
// if model == nil {
|
||||||
|
// return fmt.Errorf("nil model")
|
||||||
|
// }
|
||||||
|
// if flavor == nil {
|
||||||
|
// return fmt.Errorf("nil flavor")
|
||||||
|
// }
|
||||||
|
// id := conversion.StringValueToPointer(flavor.Id)
|
||||||
|
// if id == nil {
|
||||||
|
// return fmt.Errorf("nil flavor ID")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// flavor.Id = types.StringValue("")
|
||||||
|
//
|
||||||
|
// projectId := model.ProjectId.ValueString()
|
||||||
|
// region := model.Region.ValueString()
|
||||||
|
//
|
||||||
|
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// avl := ""
|
||||||
|
// for _, f := range flavorList {
|
||||||
|
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *f.Id == *id {
|
||||||
|
// flavor.Id = types.StringValue(*f.Id)
|
||||||
|
// flavor.Description = types.StringValue(*f.Description)
|
||||||
|
// flavor.CPU = types.Int64Value(*f.Cpu)
|
||||||
|
// flavor.RAM = types.Int64Value(*f.Memory)
|
||||||
|
// flavor.NodeType = types.StringValue(*f.NodeType)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||||
|
// }
|
||||||
|
// if flavor.Id.ValueString() == "" {
|
||||||
|
// return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package postgresFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
|
||||||
|
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
var (
|
||||||
|
_ datasource.DataSource = &flavorListDataSource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFlavorListDataSource is a helper function to simplify the provider implementation.
|
||||||
|
func NewFlavorListDataSource() datasource.DataSource {
|
||||||
|
return &flavorListDataSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flavorDataSource is the data source implementation.
|
||||||
|
type flavorListDataSource struct {
|
||||||
|
client *postgresflexalpha.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the data source type name.
|
||||||
|
func (r *flavorListDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||||
|
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavorlist"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure adds the provider configured client to the data source.
|
||||||
|
func (r *flavorListDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.client = apiClient
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors client configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorListDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
|
resp.Schema = postgresflex.FlavorDataSourceSchema(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorListDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
|
var model postgresflex.FlavorModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
// Set refreshed state
|
||||||
|
diags = resp.State.Set(ctx, model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors read")
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,514 +0,0 @@
|
||||||
package postgresflex
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFlavorType_Equal(t1 *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ObjectType basetypes.ObjectType
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
o attr.Type
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t1.Run(tt.name, func(t1 *testing.T) {
|
|
||||||
t := FlavorType{
|
|
||||||
ObjectType: tt.fields.ObjectType,
|
|
||||||
}
|
|
||||||
if got := t.Equal(tt.args.o); got != tt.want {
|
|
||||||
t1.Errorf("Equal() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorType_String(t1 *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ObjectType basetypes.ObjectType
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t1.Run(tt.name, func(t1 *testing.T) {
|
|
||||||
t := FlavorType{
|
|
||||||
ObjectType: tt.fields.ObjectType,
|
|
||||||
}
|
|
||||||
if got := t.String(); got != tt.want {
|
|
||||||
t1.Errorf("String() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorType_ValueFromObject(t1 *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ObjectType basetypes.ObjectType
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
in0 context.Context
|
|
||||||
in basetypes.ObjectValue
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want basetypes.ObjectValuable
|
|
||||||
want1 diag.Diagnostics
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t1.Run(tt.name, func(t1 *testing.T) {
|
|
||||||
t := FlavorType{
|
|
||||||
ObjectType: tt.fields.ObjectType,
|
|
||||||
}
|
|
||||||
got, got1 := t.ValueFromObject(tt.args.in0, tt.args.in)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t1.Errorf("ValueFromObject() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got1, tt.want1) {
|
|
||||||
t1.Errorf("ValueFromObject() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorType_ValueFromTerraform(t1 *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ObjectType basetypes.ObjectType
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
in tftypes.Value
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want attr.Value
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t1.Run(tt.name, func(t1 *testing.T) {
|
|
||||||
t := FlavorType{
|
|
||||||
ObjectType: tt.fields.ObjectType,
|
|
||||||
}
|
|
||||||
got, err := t.ValueFromTerraform(tt.args.ctx, tt.args.in)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t1.Errorf("ValueFromTerraform() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t1.Errorf("ValueFromTerraform() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorType_ValueType(t1 *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
ObjectType basetypes.ObjectType
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
in0 context.Context
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want attr.Value
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t1.Run(tt.name, func(t1 *testing.T) {
|
|
||||||
t := FlavorType{
|
|
||||||
ObjectType: tt.fields.ObjectType,
|
|
||||||
}
|
|
||||||
if got := t.ValueType(tt.args.in0); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t1.Errorf("ValueType() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_AttributeTypes(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
in0 context.Context
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want map[string]attr.Type
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.AttributeTypes(tt.args.in0); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("AttributeTypes() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_Equal(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
o attr.Value
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.Equal(tt.args.o); got != tt.want {
|
|
||||||
t.Errorf("Equal() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_IsNull(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.IsNull(); got != tt.want {
|
|
||||||
t.Errorf("IsNull() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_IsUnknown(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.IsUnknown(); got != tt.want {
|
|
||||||
t.Errorf("IsUnknown() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_String(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.String(); got != tt.want {
|
|
||||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_ToObjectValue(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
in0 context.Context
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want basetypes.ObjectValue
|
|
||||||
want1 diag.Diagnostics
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
got, got1 := v.ToObjectValue(tt.args.in0)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("ToObjectValue() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got1, tt.want1) {
|
|
||||||
t.Errorf("ToObjectValue() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_ToTerraformValue(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want tftypes.Value
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
got, err := v.ToTerraformValue(tt.args.ctx)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("ToTerraformValue() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("ToTerraformValue() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlavorValue_Type(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Cpu basetypes.Int64Value
|
|
||||||
Description basetypes.StringValue
|
|
||||||
Id basetypes.StringValue
|
|
||||||
Ram basetypes.Int64Value
|
|
||||||
state attr.ValueState
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want attr.Type
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
v := FlavorValue{
|
|
||||||
Cpu: tt.fields.Cpu,
|
|
||||||
Description: tt.fields.Description,
|
|
||||||
Id: tt.fields.Id,
|
|
||||||
Ram: tt.fields.Ram,
|
|
||||||
state: tt.fields.state,
|
|
||||||
}
|
|
||||||
if got := v.Type(tt.args.ctx); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Type() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewFlavorValue(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
attributeTypes map[string]attr.Type
|
|
||||||
attributes map[string]attr.Value
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want FlavorValue
|
|
||||||
want1 diag.Diagnostics
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, got1 := NewFlavorValue(tt.args.attributeTypes, tt.args.attributes)
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewFlavorValue() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got1, tt.want1) {
|
|
||||||
t.Errorf("NewFlavorValue() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewFlavorValueMust(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
attributeTypes map[string]attr.Type
|
|
||||||
attributes map[string]attr.Value
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want FlavorValue
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := NewFlavorValueMust(tt.args.attributeTypes, tt.args.attributes); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewFlavorValueMust() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewFlavorValueNull(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
want FlavorValue
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := NewFlavorValueNull(); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewFlavorValueNull() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewFlavorValueUnknown(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
want FlavorValue
|
|
||||||
}{
|
|
||||||
// TODO: Add test cases.
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := NewFlavorValueUnknown(); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewFlavorValueUnknown() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package postgresflex
|
|
||||||
|
|
@ -100,25 +100,11 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
|
||||||
"backup_schedule": schema.StringAttribute{
|
"backup_schedule": schema.StringAttribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"flavor": schema.SingleNestedAttribute{
|
"retention_days": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"flavor_id": schema.StringAttribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Attributes: map[string]schema.Attribute{
|
|
||||||
"id": schema.StringAttribute{
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
"description": schema.StringAttribute{
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
"cpu": schema.Int64Attribute{
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
"ram": schema.Int64Attribute{
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
"node_type": schema.StringAttribute{
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"replicas": schema.Int64Attribute{
|
"replicas": schema.Int64Attribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|
@ -226,31 +212,6 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
|
||||||
if instanceResp != nil && instanceResp.FlavorId != nil {
|
|
||||||
flavor.Id = types.StringValue(*instanceResp.FlavorId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := getFlavorModelById(ctx, r.client, &model, flavor)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage = &storageModel{}
|
var storage = &storageModel{}
|
||||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||||
|
|
@ -278,7 +239,7 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, r.client, instanceResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
|
@ -20,9 +19,9 @@ type postgresflexClient interface {
|
||||||
|
|
||||||
func mapFields(
|
func mapFields(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
client postgresflexClient,
|
||||||
resp *postgresflex.GetInstanceResponse,
|
resp *postgresflex.GetInstanceResponse,
|
||||||
model *Model,
|
model *Model,
|
||||||
flavor *flavorModel,
|
|
||||||
storage *storageModel,
|
storage *storageModel,
|
||||||
encryption *encryptionModel,
|
encryption *encryptionModel,
|
||||||
network *networkModel,
|
network *networkModel,
|
||||||
|
|
@ -80,19 +79,11 @@ func mapFields(
|
||||||
return fmt.Errorf("creating network (acl list): %w", core.DiagsToError(diags))
|
return fmt.Errorf("creating network (acl list): %w", core.DiagsToError(diags))
|
||||||
}
|
}
|
||||||
|
|
||||||
var routerAddress string
|
|
||||||
if instance.Network.RouterAddress != nil {
|
|
||||||
routerAddress = *instance.Network.RouterAddress
|
|
||||||
diags.AddWarning("field missing while mapping fields", "router_address was empty in API response")
|
|
||||||
}
|
|
||||||
if instance.Network.InstanceAddress == nil {
|
|
||||||
return fmt.Errorf("creating network: no instance address returned")
|
|
||||||
}
|
|
||||||
networkValues = map[string]attr.Value{
|
networkValues = map[string]attr.Value{
|
||||||
"acl": aclList,
|
"acl": aclList,
|
||||||
"access_scope": types.StringValue(string(*instance.Network.AccessScope)),
|
"access_scope": types.StringPointerValue((*string)(instance.Network.AccessScope)),
|
||||||
"instance_address": types.StringValue(*instance.Network.InstanceAddress),
|
"instance_address": types.StringPointerValue(instance.Network.InstanceAddress),
|
||||||
"router_address": types.StringValue(routerAddress),
|
"router_address": types.StringPointerValue(instance.Network.RouterAddress),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
networkObject, diags := types.ObjectValue(networkTypes, networkValues)
|
networkObject, diags := types.ObjectValue(networkTypes, networkValues)
|
||||||
|
|
@ -100,48 +91,6 @@ func mapFields(
|
||||||
return fmt.Errorf("creating network: %w", core.DiagsToError(diags))
|
return fmt.Errorf("creating network: %w", core.DiagsToError(diags))
|
||||||
}
|
}
|
||||||
|
|
||||||
var flavorValues map[string]attr.Value
|
|
||||||
if instance.FlavorId == nil || *instance.FlavorId == "" {
|
|
||||||
return fmt.Errorf("instance has no flavor id")
|
|
||||||
}
|
|
||||||
if !flavor.Id.IsUnknown() && !flavor.Id.IsNull() {
|
|
||||||
if *instance.FlavorId != flavor.Id.ValueString() {
|
|
||||||
return fmt.Errorf("instance has different flavor id %s - %s", *instance.FlavorId, flavor.Id.ValueString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if model.Flavor.IsNull() || model.Flavor.IsUnknown() {
|
|
||||||
var nodeType string
|
|
||||||
if flavor.NodeType.IsUnknown() || flavor.NodeType.IsNull() {
|
|
||||||
if instance.Replicas == nil {
|
|
||||||
return fmt.Errorf("instance has no replicas setting")
|
|
||||||
}
|
|
||||||
switch *instance.Replicas {
|
|
||||||
case 1:
|
|
||||||
nodeType = "Single"
|
|
||||||
case 3:
|
|
||||||
nodeType = "Replicas"
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("could not determine replicas settings")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nodeType = flavor.NodeType.ValueString()
|
|
||||||
}
|
|
||||||
flavorValues = map[string]attr.Value{
|
|
||||||
"id": flavor.Id,
|
|
||||||
"description": flavor.Description,
|
|
||||||
"cpu": flavor.CPU,
|
|
||||||
"ram": flavor.RAM,
|
|
||||||
"node_type": types.StringValue(nodeType),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flavorValues = model.Flavor.Attributes()
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorObject, diags := types.ObjectValue(flavorTypes, flavorValues)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("creating flavor: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageValues map[string]attr.Value
|
var storageValues map[string]attr.Value
|
||||||
if instance.Storage == nil {
|
if instance.Storage == nil {
|
||||||
storageValues = map[string]attr.Value{
|
storageValues = map[string]attr.Value{
|
||||||
|
|
@ -167,10 +116,8 @@ func mapFields(
|
||||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
|
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
|
||||||
model.InstanceId = types.StringValue(instanceId)
|
model.InstanceId = types.StringValue(instanceId)
|
||||||
model.Name = types.StringPointerValue(instance.Name)
|
model.Name = types.StringPointerValue(instance.Name)
|
||||||
model.Network = networkObject
|
|
||||||
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
|
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
|
||||||
model.Flavor = flavorObject
|
model.FlavorId = types.StringPointerValue(instance.FlavorId)
|
||||||
// TODO - verify working
|
|
||||||
model.Replicas = types.Int64Value(int64(*instance.Replicas))
|
model.Replicas = types.Int64Value(int64(*instance.Replicas))
|
||||||
model.Storage = storageObject
|
model.Storage = storageObject
|
||||||
model.Version = types.StringPointerValue(instance.Version)
|
model.Version = types.StringPointerValue(instance.Version)
|
||||||
|
|
@ -182,7 +129,6 @@ func mapFields(
|
||||||
|
|
||||||
func toCreatePayload(
|
func toCreatePayload(
|
||||||
model *Model,
|
model *Model,
|
||||||
flavor *flavorModel,
|
|
||||||
storage *storageModel,
|
storage *storageModel,
|
||||||
enc *encryptionModel,
|
enc *encryptionModel,
|
||||||
net *networkModel,
|
net *networkModel,
|
||||||
|
|
@ -190,9 +136,6 @@ func toCreatePayload(
|
||||||
if model == nil {
|
if model == nil {
|
||||||
return nil, fmt.Errorf("nil model")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
if flavor == nil {
|
|
||||||
return nil, fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
return nil, fmt.Errorf("nil storage")
|
return nil, fmt.Errorf("nil storage")
|
||||||
}
|
}
|
||||||
|
|
@ -239,24 +182,11 @@ func toCreatePayload(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
|
|
||||||
if !flavor.NodeType.IsNull() && !flavor.NodeType.IsUnknown() {
|
|
||||||
switch strings.ToLower(flavor.NodeType.ValueString()) {
|
|
||||||
case "single":
|
|
||||||
replVal = int32(1)
|
|
||||||
case "replica":
|
|
||||||
replVal = int32(3)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("flavor has invalid replica attribute")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &postgresflex.CreateInstanceRequestPayload{
|
return &postgresflex.CreateInstanceRequestPayload{
|
||||||
Acl: &aclElements,
|
Acl: &aclElements,
|
||||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||||
Encryption: encryptionPayload,
|
Encryption: encryptionPayload,
|
||||||
FlavorId: conversion.StringValueToPointer(flavor.Id),
|
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||||
Name: conversion.StringValueToPointer(model.Name),
|
Name: conversion.StringValueToPointer(model.Name),
|
||||||
Network: networkPayload,
|
Network: networkPayload,
|
||||||
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(&replVal),
|
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(&replVal),
|
||||||
|
|
@ -266,13 +196,14 @@ func toCreatePayload(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toUpdatePayload(model *Model, flavor *flavorModel, storage *storageModel, _ *networkModel) (*postgresflex.UpdateInstancePartiallyRequestPayload, error) {
|
func toUpdatePayload(
|
||||||
|
model *Model,
|
||||||
|
storage *storageModel,
|
||||||
|
_ *networkModel,
|
||||||
|
) (*postgresflex.UpdateInstancePartiallyRequestPayload, error) {
|
||||||
if model == nil {
|
if model == nil {
|
||||||
return nil, fmt.Errorf("nil model")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
if flavor == nil {
|
|
||||||
return nil, fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
return nil, fmt.Errorf("nil storage")
|
return nil, fmt.Errorf("nil storage")
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +213,7 @@ func toUpdatePayload(model *Model, flavor *flavorModel, storage *storageModel, _
|
||||||
// Items: &acl,
|
// Items: &acl,
|
||||||
// },
|
// },
|
||||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||||
FlavorId: conversion.StringValueToPointer(flavor.Id),
|
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||||
Name: conversion.StringValueToPointer(model.Name),
|
Name: conversion.StringValueToPointer(model.Name),
|
||||||
// Replicas: conversion.Int64ValueToPointer(model.Replicas),
|
// Replicas: conversion.Int64ValueToPointer(model.Replicas),
|
||||||
Storage: &postgresflex.StorageUpdate{
|
Storage: &postgresflex.StorageUpdate{
|
||||||
|
|
@ -291,187 +222,3 @@ func toUpdatePayload(model *Model, flavor *flavorModel, storage *storageModel, _
|
||||||
Version: conversion.StringValueToPointer(model.Version),
|
Version: conversion.StringValueToPointer(model.Version),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFlavorId(ctx context.Context, client postgresflexClient, model *Model, flavor *flavorModel, storage *storageModel) error {
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
if flavor == nil {
|
|
||||||
return fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
cpu := flavor.CPU.ValueInt64()
|
|
||||||
if cpu == 0 {
|
|
||||||
return fmt.Errorf("nil CPU")
|
|
||||||
}
|
|
||||||
ram := flavor.RAM.ValueInt64()
|
|
||||||
if ram == 0 {
|
|
||||||
return fmt.Errorf("nil RAM")
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeType := flavor.NodeType.ValueString()
|
|
||||||
if nodeType == "" {
|
|
||||||
if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
|
|
||||||
return fmt.Errorf("nil NodeType")
|
|
||||||
}
|
|
||||||
switch model.Replicas.ValueInt64() {
|
|
||||||
case 1:
|
|
||||||
nodeType = "Single"
|
|
||||||
case 3:
|
|
||||||
nodeType = "Replica"
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storageClass := conversion.StringValueToPointer(storage.Class)
|
|
||||||
if storageClass == nil {
|
|
||||||
return fmt.Errorf("nil StorageClass")
|
|
||||||
}
|
|
||||||
storageSize := conversion.Int64ValueToPointer(storage.Size)
|
|
||||||
if storageSize == nil {
|
|
||||||
return fmt.Errorf("nil StorageSize")
|
|
||||||
}
|
|
||||||
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
region := model.Region.ValueString()
|
|
||||||
|
|
||||||
flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
avl := ""
|
|
||||||
foundFlavorCount := 0
|
|
||||||
var foundFlavors []string
|
|
||||||
for _, f := range flavorList {
|
|
||||||
if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(*f.NodeType, nodeType) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *f.Cpu == cpu && *f.Memory == ram {
|
|
||||||
var useSc *postgresflex.FlavorStorageClassesStorageClass
|
|
||||||
for _, sc := range *f.StorageClasses {
|
|
||||||
if *sc.Class != *storageClass {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
|
|
||||||
return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
|
|
||||||
}
|
|
||||||
useSc = &sc
|
|
||||||
}
|
|
||||||
if useSc == nil {
|
|
||||||
return fmt.Errorf("no storage class found for %s", *storageClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
flavor.Id = types.StringValue(*f.Id)
|
|
||||||
flavor.Description = types.StringValue(*f.Description)
|
|
||||||
foundFlavors = append(foundFlavors, fmt.Sprintf("%s (%d/%d - %s)", *f.Id, *f.Cpu, *f.Memory, *f.NodeType))
|
|
||||||
foundFlavorCount++
|
|
||||||
}
|
|
||||||
for _, cls := range *f.StorageClasses {
|
|
||||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundFlavorCount > 1 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"number of flavors returned: %d\nmultiple flavors found: %d flavors\n %s",
|
|
||||||
len(flavorList),
|
|
||||||
foundFlavorCount,
|
|
||||||
strings.Join(foundFlavors, "\n "),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if flavor.Id.ValueString() == "" {
|
|
||||||
return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAllFlavors(ctx context.Context, client postgresflexClient, projectId, region string) ([]postgresflex.ListFlavors, error) {
|
|
||||||
if projectId == "" || region == "" {
|
|
||||||
return nil, fmt.Errorf("listing postgresflex flavors: projectId and region are required")
|
|
||||||
}
|
|
||||||
var flavorList []postgresflex.ListFlavors
|
|
||||||
|
|
||||||
page := int64(1)
|
|
||||||
size := int64(10)
|
|
||||||
sort := postgresflex.FLAVORSORT_INDEX_ASC
|
|
||||||
counter := 0
|
|
||||||
for {
|
|
||||||
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("listing postgresflex flavors: %w", err)
|
|
||||||
}
|
|
||||||
if res.Flavors == nil {
|
|
||||||
return nil, fmt.Errorf("finding flavors for project %s", projectId)
|
|
||||||
}
|
|
||||||
pagination := res.GetPagination()
|
|
||||||
flavors := res.GetFlavors()
|
|
||||||
flavorList = append(flavorList, flavors...)
|
|
||||||
|
|
||||||
if *pagination.TotalRows < int64(len(flavorList)) {
|
|
||||||
return nil, fmt.Errorf("total rows is smaller than current accumulated list - that should not happen")
|
|
||||||
}
|
|
||||||
if *pagination.TotalRows == int64(len(flavorList)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
page++
|
|
||||||
|
|
||||||
if page > *pagination.TotalPages {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement a breakpoint
|
|
||||||
counter++
|
|
||||||
if counter > 1000 {
|
|
||||||
panic("too many pagination results")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flavorList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFlavorModelById(ctx context.Context, client postgresflexClient, model *Model, flavor *flavorModel) error {
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
if flavor == nil {
|
|
||||||
return fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
id := conversion.StringValueToPointer(flavor.Id)
|
|
||||||
if id == nil {
|
|
||||||
return fmt.Errorf("nil flavor ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
flavor.Id = types.StringValue("")
|
|
||||||
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
region := model.Region.ValueString()
|
|
||||||
|
|
||||||
flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
avl := ""
|
|
||||||
for _, f := range flavorList {
|
|
||||||
if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *f.Id == *id {
|
|
||||||
flavor.Id = types.StringValue(*f.Id)
|
|
||||||
flavor.Description = types.StringValue(*f.Description)
|
|
||||||
flavor.CPU = types.Int64Value(*f.Cpu)
|
|
||||||
flavor.RAM = types.Int64Value(*f.Memory)
|
|
||||||
flavor.NodeType = types.StringValue(*f.NodeType)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
|
||||||
}
|
|
||||||
if flavor.Id.ValueString() == "" {
|
|
||||||
return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ package postgresflexalpha
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
)
|
)
|
||||||
|
|
@ -490,327 +486,303 @@ func (c postgresFlexClientMocked) GetFlavorsRequestExecute(
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getAllFlavors(t *testing.T) {
|
//func Test_getAllFlavors(t *testing.T) {
|
||||||
type args struct {
|
// type args struct {
|
||||||
projectId string
|
// projectId string
|
||||||
region string
|
// region string
|
||||||
}
|
// }
|
||||||
tests := []struct {
|
// tests := []struct {
|
||||||
name string
|
// name string
|
||||||
args args
|
// args args
|
||||||
firstItem int
|
// firstItem int
|
||||||
lastItem int
|
// lastItem int
|
||||||
want []postgresflex.ListFlavors
|
// want []postgresflex.ListFlavors
|
||||||
wantErr bool
|
// wantErr bool
|
||||||
}{
|
// }{
|
||||||
{
|
// {
|
||||||
name: "find exactly one flavor",
|
// name: "find exactly one flavor",
|
||||||
args: args{
|
// args: args{
|
||||||
projectId: "project",
|
// projectId: "project",
|
||||||
region: "region",
|
// region: "region",
|
||||||
},
|
// },
|
||||||
firstItem: 0,
|
// firstItem: 0,
|
||||||
lastItem: 0,
|
// lastItem: 0,
|
||||||
want: []postgresflex.ListFlavors{
|
// want: []postgresflex.ListFlavors{
|
||||||
testFlavorToResponseFlavor(responseList[0]),
|
// testFlavorToResponseFlavor(responseList[0]),
|
||||||
},
|
// },
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "get exactly 1 page flavors",
|
// name: "get exactly 1 page flavors",
|
||||||
args: args{
|
// args: args{
|
||||||
projectId: "project",
|
// projectId: "project",
|
||||||
region: "region",
|
// region: "region",
|
||||||
},
|
// },
|
||||||
firstItem: 0,
|
// firstItem: 0,
|
||||||
lastItem: 9,
|
// lastItem: 9,
|
||||||
want: testFlavorListToResponseFlavorList(responseList[0:10]),
|
// want: testFlavorListToResponseFlavorList(responseList[0:10]),
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "get exactly 20 flavors",
|
// name: "get exactly 20 flavors",
|
||||||
args: args{
|
// args: args{
|
||||||
projectId: "project",
|
// projectId: "project",
|
||||||
region: "region",
|
// region: "region",
|
||||||
},
|
// },
|
||||||
firstItem: 0,
|
// firstItem: 0,
|
||||||
lastItem: 20,
|
// lastItem: 20,
|
||||||
// 0 indexed therefore we want :21
|
// // 0 indexed therefore we want :21
|
||||||
want: testFlavorListToResponseFlavorList(responseList[0:21]),
|
// want: testFlavorListToResponseFlavorList(responseList[0:21]),
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "get all flavors",
|
// name: "get all flavors",
|
||||||
args: args{
|
// args: args{
|
||||||
projectId: "project",
|
// projectId: "project",
|
||||||
region: "region",
|
// region: "region",
|
||||||
},
|
// },
|
||||||
firstItem: 0,
|
// firstItem: 0,
|
||||||
lastItem: len(responseList),
|
// lastItem: len(responseList),
|
||||||
want: testFlavorListToResponseFlavorList(responseList),
|
// want: testFlavorListToResponseFlavorList(responseList),
|
||||||
wantErr: false,
|
// wantErr: 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) {
|
||||||
first := tt.firstItem
|
// first := tt.firstItem
|
||||||
if first > len(responseList)-1 {
|
// if first > len(responseList)-1 {
|
||||||
first = len(responseList) - 1
|
// first = len(responseList) - 1
|
||||||
}
|
// }
|
||||||
last := tt.lastItem
|
// last := tt.lastItem
|
||||||
if last > len(responseList)-1 {
|
// if last > len(responseList)-1 {
|
||||||
last = len(responseList) - 1
|
// last = len(responseList) - 1
|
||||||
}
|
// }
|
||||||
mockClient := postgresFlexClientMocked{
|
// mockClient := postgresFlexClientMocked{
|
||||||
returnError: tt.wantErr,
|
// returnError: tt.wantErr,
|
||||||
firstItem: first,
|
// firstItem: first,
|
||||||
lastItem: last,
|
// lastItem: last,
|
||||||
}
|
// }
|
||||||
got, err := getAllFlavors(context.TODO(), mockClient, tt.args.projectId, tt.args.region)
|
// got, err := getAllFlavors(context.TODO(), mockClient, tt.args.projectId, tt.args.region)
|
||||||
if (err != nil) != tt.wantErr {
|
// if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("getAllFlavors() error = %v, wantErr %v", err, tt.wantErr)
|
// t.Errorf("getAllFlavors() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||||
|
// t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
// t.Errorf("getAllFlavors() got = %v, want %v", got, tt.want)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
//func Test_loadFlavorId(t *testing.T) {
|
||||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
// type args struct {
|
||||||
}
|
// ctx context.Context
|
||||||
|
// model *Model
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
// storage *storageModel
|
||||||
t.Errorf("getAllFlavors() got = %v, want %v", got, tt.want)
|
// }
|
||||||
}
|
// tests := []struct {
|
||||||
})
|
// name string
|
||||||
}
|
// args args
|
||||||
}
|
// firstItem int
|
||||||
|
// lastItem int
|
||||||
func Test_loadFlavorId(t *testing.T) {
|
// want []postgresflex.ListFlavors
|
||||||
type args struct {
|
// wantErr bool
|
||||||
ctx context.Context
|
// }{
|
||||||
model *Model
|
// {
|
||||||
flavor *flavorModel
|
// name: "find a single flavor",
|
||||||
storage *storageModel
|
// args: args{
|
||||||
}
|
// ctx: context.Background(),
|
||||||
tests := []struct {
|
// model: &Model{
|
||||||
name string
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
args args
|
// Region: basetypes.NewStringValue("region"),
|
||||||
firstItem int
|
// },
|
||||||
lastItem int
|
// storage: &storageModel{
|
||||||
want []postgresflex.ListFlavors
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
wantErr bool
|
// Size: basetypes.NewInt64Value(100),
|
||||||
}{
|
// },
|
||||||
{
|
// },
|
||||||
name: "find a single flavor",
|
// firstItem: 0,
|
||||||
args: args{
|
// lastItem: 3,
|
||||||
ctx: context.Background(),
|
// want: []postgresflex.ListFlavors{
|
||||||
model: &Model{
|
// testFlavorToResponseFlavor(responseList[0]),
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// },
|
||||||
Region: basetypes.NewStringValue("region"),
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
flavor: &flavorModel{
|
// {
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// name: "find a single flavor by replicas option",
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// args: args{
|
||||||
NodeType: basetypes.NewStringValue("Single"),
|
// ctx: context.Background(),
|
||||||
},
|
// model: &Model{
|
||||||
storage: &storageModel{
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// Region: basetypes.NewStringValue("region"),
|
||||||
Size: basetypes.NewInt64Value(100),
|
// Replicas: basetypes.NewInt64Value(1),
|
||||||
},
|
// },
|
||||||
},
|
// storage: &storageModel{
|
||||||
firstItem: 0,
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
lastItem: 3,
|
// Size: basetypes.NewInt64Value(100),
|
||||||
want: []postgresflex.ListFlavors{
|
// },
|
||||||
testFlavorToResponseFlavor(responseList[0]),
|
// },
|
||||||
},
|
// firstItem: 0,
|
||||||
wantErr: false,
|
// lastItem: 3,
|
||||||
},
|
// want: []postgresflex.ListFlavors{
|
||||||
{
|
// testFlavorToResponseFlavor(responseList[0]),
|
||||||
name: "find a single flavor by replicas option",
|
// },
|
||||||
args: args{
|
// wantErr: false,
|
||||||
ctx: context.Background(),
|
// },
|
||||||
model: &Model{
|
// {
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// name: "fail finding find a single flavor by replicas option",
|
||||||
Region: basetypes.NewStringValue("region"),
|
// args: args{
|
||||||
Replicas: basetypes.NewInt64Value(1),
|
// ctx: context.Background(),
|
||||||
},
|
// model: &Model{
|
||||||
flavor: &flavorModel{
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// Region: basetypes.NewStringValue("region"),
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// Replicas: basetypes.NewInt64Value(1),
|
||||||
},
|
// },
|
||||||
storage: &storageModel{
|
// storage: &storageModel{
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
Size: basetypes.NewInt64Value(100),
|
// Size: basetypes.NewInt64Value(100),
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
firstItem: 0,
|
// firstItem: 13,
|
||||||
lastItem: 3,
|
// lastItem: 23,
|
||||||
want: []postgresflex.ListFlavors{
|
// want: []postgresflex.ListFlavors{},
|
||||||
testFlavorToResponseFlavor(responseList[0]),
|
// wantErr: true,
|
||||||
},
|
// },
|
||||||
wantErr: false,
|
// {
|
||||||
},
|
// name: "find a replicas flavor lower case",
|
||||||
{
|
// args: args{
|
||||||
name: "fail finding find a single flavor by replicas option",
|
// ctx: context.Background(),
|
||||||
args: args{
|
// model: &Model{
|
||||||
ctx: context.Background(),
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
model: &Model{
|
// Region: basetypes.NewStringValue("region"),
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// },
|
||||||
Region: basetypes.NewStringValue("region"),
|
// storage: &storageModel{
|
||||||
Replicas: basetypes.NewInt64Value(1),
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
},
|
// Size: basetypes.NewInt64Value(100),
|
||||||
flavor: &flavorModel{
|
// },
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// },
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// firstItem: 0,
|
||||||
},
|
// lastItem: len(responseList) - 1,
|
||||||
storage: &storageModel{
|
// want: []postgresflex.ListFlavors{
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// testFlavorToResponseFlavor(responseList[16]),
|
||||||
Size: basetypes.NewInt64Value(100),
|
// },
|
||||||
},
|
// wantErr: false,
|
||||||
},
|
// },
|
||||||
firstItem: 13,
|
// {
|
||||||
lastItem: 23,
|
// name: "find a replicas flavor CamelCase",
|
||||||
want: []postgresflex.ListFlavors{},
|
// args: args{
|
||||||
wantErr: true,
|
// ctx: context.Background(),
|
||||||
},
|
// model: &Model{
|
||||||
{
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
name: "find a replicas flavor lower case",
|
// Region: basetypes.NewStringValue("region"),
|
||||||
args: args{
|
// },
|
||||||
ctx: context.Background(),
|
// storage: &storageModel{
|
||||||
model: &Model{
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// Size: basetypes.NewInt64Value(100),
|
||||||
Region: basetypes.NewStringValue("region"),
|
// },
|
||||||
},
|
// },
|
||||||
flavor: &flavorModel{
|
// firstItem: 0,
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// lastItem: len(responseList) - 1,
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// want: []postgresflex.ListFlavors{
|
||||||
NodeType: basetypes.NewStringValue("replica"),
|
// testFlavorToResponseFlavor(responseList[16]),
|
||||||
},
|
// },
|
||||||
storage: &storageModel{
|
// wantErr: false,
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// },
|
||||||
Size: basetypes.NewInt64Value(100),
|
// {
|
||||||
},
|
// name: "find a replicas flavor by replicas option",
|
||||||
},
|
// args: args{
|
||||||
firstItem: 0,
|
// ctx: context.Background(),
|
||||||
lastItem: len(responseList) - 1,
|
// model: &Model{
|
||||||
want: []postgresflex.ListFlavors{
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
testFlavorToResponseFlavor(responseList[16]),
|
// Region: basetypes.NewStringValue("region"),
|
||||||
},
|
// Replicas: basetypes.NewInt64Value(3),
|
||||||
wantErr: false,
|
// },
|
||||||
},
|
// flavor: &flavorModel{
|
||||||
{
|
// CPU: basetypes.NewInt64Value(1),
|
||||||
name: "find a replicas flavor CamelCase",
|
// RAM: basetypes.NewInt64Value(1),
|
||||||
args: args{
|
// },
|
||||||
ctx: context.Background(),
|
// storage: &storageModel{
|
||||||
model: &Model{
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// Size: basetypes.NewInt64Value(100),
|
||||||
Region: basetypes.NewStringValue("region"),
|
// },
|
||||||
},
|
// },
|
||||||
flavor: &flavorModel{
|
// firstItem: 0,
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// lastItem: len(responseList) - 1,
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// want: []postgresflex.ListFlavors{
|
||||||
NodeType: basetypes.NewStringValue("Replica"),
|
// testFlavorToResponseFlavor(responseList[16]),
|
||||||
},
|
// },
|
||||||
storage: &storageModel{
|
// wantErr: false,
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// },
|
||||||
Size: basetypes.NewInt64Value(100),
|
// {
|
||||||
},
|
// name: "fail finding a replica flavor",
|
||||||
},
|
// args: args{
|
||||||
firstItem: 0,
|
// ctx: context.Background(),
|
||||||
lastItem: len(responseList) - 1,
|
// model: &Model{
|
||||||
want: []postgresflex.ListFlavors{
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
testFlavorToResponseFlavor(responseList[16]),
|
// Region: basetypes.NewStringValue("region"),
|
||||||
},
|
// Replicas: basetypes.NewInt64Value(3),
|
||||||
wantErr: false,
|
// },
|
||||||
},
|
// flavor: &flavorModel{
|
||||||
{
|
// CPU: basetypes.NewInt64Value(1),
|
||||||
name: "find a replicas flavor by replicas option",
|
// RAM: basetypes.NewInt64Value(1),
|
||||||
args: args{
|
// },
|
||||||
ctx: context.Background(),
|
// storage: &storageModel{
|
||||||
model: &Model{
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// Size: basetypes.NewInt64Value(100),
|
||||||
Region: basetypes.NewStringValue("region"),
|
// },
|
||||||
Replicas: basetypes.NewInt64Value(3),
|
// },
|
||||||
},
|
// firstItem: 0,
|
||||||
flavor: &flavorModel{
|
// lastItem: 10,
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// want: []postgresflex.ListFlavors{},
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// wantErr: true,
|
||||||
},
|
// },
|
||||||
storage: &storageModel{
|
// {
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// name: "no flavor found error",
|
||||||
Size: basetypes.NewInt64Value(100),
|
// args: args{
|
||||||
},
|
// ctx: context.Background(),
|
||||||
},
|
// model: &Model{
|
||||||
firstItem: 0,
|
// ProjectId: basetypes.NewStringValue("project"),
|
||||||
lastItem: len(responseList) - 1,
|
// Region: basetypes.NewStringValue("region"),
|
||||||
want: []postgresflex.ListFlavors{
|
// },
|
||||||
testFlavorToResponseFlavor(responseList[16]),
|
// flavor: &flavorModel{
|
||||||
},
|
// CPU: basetypes.NewInt64Value(10),
|
||||||
wantErr: false,
|
// RAM: basetypes.NewInt64Value(1000),
|
||||||
},
|
// NodeType: basetypes.NewStringValue("Single"),
|
||||||
{
|
// },
|
||||||
name: "fail finding a replica flavor",
|
// storage: &storageModel{
|
||||||
args: args{
|
// Class: basetypes.NewStringValue("sc1"),
|
||||||
ctx: context.Background(),
|
// Size: basetypes.NewInt64Value(100),
|
||||||
model: &Model{
|
// },
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// },
|
||||||
Region: basetypes.NewStringValue("region"),
|
// firstItem: 0,
|
||||||
Replicas: basetypes.NewInt64Value(3),
|
// lastItem: 3,
|
||||||
},
|
// want: []postgresflex.ListFlavors{},
|
||||||
flavor: &flavorModel{
|
// wantErr: true,
|
||||||
CPU: basetypes.NewInt64Value(1),
|
// },
|
||||||
RAM: basetypes.NewInt64Value(1),
|
// }
|
||||||
},
|
// for _, tt := range tests {
|
||||||
storage: &storageModel{
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
// first := tt.firstItem
|
||||||
Size: basetypes.NewInt64Value(100),
|
// if first > len(responseList)-1 {
|
||||||
},
|
// first = len(responseList) - 1
|
||||||
},
|
// }
|
||||||
firstItem: 0,
|
// last := tt.lastItem
|
||||||
lastItem: 10,
|
// if last > len(responseList)-1 {
|
||||||
want: []postgresflex.ListFlavors{},
|
// last = len(responseList) - 1
|
||||||
wantErr: true,
|
// }
|
||||||
},
|
// mockClient := postgresFlexClientMocked{
|
||||||
{
|
// returnError: tt.wantErr,
|
||||||
name: "no flavor found error",
|
// firstItem: first,
|
||||||
args: args{
|
// lastItem: last,
|
||||||
ctx: context.Background(),
|
// }
|
||||||
model: &Model{
|
// if err := loadFlavorId(tt.args.ctx, mockClient, tt.args.model, tt.args.flavor, tt.args.storage); (err != nil) != tt.wantErr {
|
||||||
ProjectId: basetypes.NewStringValue("project"),
|
// t.Errorf("loadFlavorId() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
Region: basetypes.NewStringValue("region"),
|
// }
|
||||||
},
|
// })
|
||||||
flavor: &flavorModel{
|
// }
|
||||||
CPU: basetypes.NewInt64Value(10),
|
//}
|
||||||
RAM: basetypes.NewInt64Value(1000),
|
|
||||||
NodeType: basetypes.NewStringValue("Single"),
|
|
||||||
},
|
|
||||||
storage: &storageModel{
|
|
||||||
Class: basetypes.NewStringValue("sc1"),
|
|
||||||
Size: basetypes.NewInt64Value(100),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
firstItem: 0,
|
|
||||||
lastItem: 3,
|
|
||||||
want: []postgresflex.ListFlavors{},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
first := tt.firstItem
|
|
||||||
if first > len(responseList)-1 {
|
|
||||||
first = len(responseList) - 1
|
|
||||||
}
|
|
||||||
last := tt.lastItem
|
|
||||||
if last > len(responseList)-1 {
|
|
||||||
last = len(responseList) - 1
|
|
||||||
}
|
|
||||||
mockClient := postgresFlexClientMocked{
|
|
||||||
returnError: tt.wantErr,
|
|
||||||
firstItem: first,
|
|
||||||
lastItem: last,
|
|
||||||
}
|
|
||||||
if err := loadFlavorId(tt.args.ctx, mockClient, tt.args.model, tt.args.flavor, tt.args.storage); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("loadFlavorId() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ type Model struct {
|
||||||
ProjectId types.String `tfsdk:"project_id"`
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
Name types.String `tfsdk:"name"`
|
Name types.String `tfsdk:"name"`
|
||||||
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
||||||
Flavor types.Object `tfsdk:"flavor"`
|
FlavorId types.String `tfsdk:"flavor_id"`
|
||||||
Replicas types.Int64 `tfsdk:"replicas"`
|
Replicas types.Int64 `tfsdk:"replicas"`
|
||||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||||
Storage types.Object `tfsdk:"storage"`
|
Storage types.Object `tfsdk:"storage"`
|
||||||
|
|
@ -22,9 +22,11 @@ type Model struct {
|
||||||
Network types.Object `tfsdk:"network"`
|
Network types.Object `tfsdk:"network"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IdentityModel struct {
|
//type IdentityModel struct {
|
||||||
ID types.String `tfsdk:"id"`
|
// InstanceId types.String `tfsdk:"instance_id"`
|
||||||
}
|
// Region types.String `tfsdk:"region"`
|
||||||
|
// ProjectId types.String `tfsdk:"project_id"`
|
||||||
|
//}
|
||||||
|
|
||||||
type encryptionModel struct {
|
type encryptionModel struct {
|
||||||
KeyRingId types.String `tfsdk:"keyring_id"`
|
KeyRingId types.String `tfsdk:"keyring_id"`
|
||||||
|
|
@ -54,24 +56,6 @@ var networkTypes = map[string]attr.Type{
|
||||||
"router_address": basetypes.StringType{},
|
"router_address": basetypes.StringType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct corresponding to Model.Flavor
|
|
||||||
type flavorModel struct {
|
|
||||||
Id types.String `tfsdk:"id"`
|
|
||||||
Description types.String `tfsdk:"description"`
|
|
||||||
CPU types.Int64 `tfsdk:"cpu"`
|
|
||||||
RAM types.Int64 `tfsdk:"ram"`
|
|
||||||
NodeType types.String `tfsdk:"node_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types corresponding to flavorModel
|
|
||||||
var flavorTypes = map[string]attr.Type{
|
|
||||||
"id": basetypes.StringType{},
|
|
||||||
"description": basetypes.StringType{},
|
|
||||||
"cpu": basetypes.Int64Type{},
|
|
||||||
"ram": basetypes.Int64Type{},
|
|
||||||
"node_type": basetypes.StringType{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct corresponding to Model.Storage
|
// Struct corresponding to Model.Storage
|
||||||
type storageModel struct {
|
type storageModel struct {
|
||||||
Class types.String `tfsdk:"class"`
|
Class types.String `tfsdk:"class"`
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,13 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
|
||||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||||
|
|
||||||
"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/resource/schema/listplanmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
|
@ -42,7 +39,7 @@ var (
|
||||||
_ resource.ResourceWithImportState = &instanceResource{}
|
_ resource.ResourceWithImportState = &instanceResource{}
|
||||||
_ resource.ResourceWithModifyPlan = &instanceResource{}
|
_ resource.ResourceWithModifyPlan = &instanceResource{}
|
||||||
_ resource.ResourceWithValidateConfig = &instanceResource{}
|
_ resource.ResourceWithValidateConfig = &instanceResource{}
|
||||||
_ resource.ResourceWithIdentity = &instanceResource{}
|
//_ resource.ResourceWithIdentity = &instanceResource{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewInstanceResource is a helper function to simplify the provider implementation.
|
// NewInstanceResource is a helper function to simplify the provider implementation.
|
||||||
|
|
@ -128,35 +125,30 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
|
||||||
// Schema defines the schema for the resource.
|
// Schema defines the schema for the resource.
|
||||||
func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
descriptions := map[string]string{
|
descriptions := map[string]string{
|
||||||
"main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.",
|
"main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.",
|
||||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
|
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
|
||||||
"instance_id": "ID of the PostgresFlex instance.",
|
"instance_id": "ID of the PostgresFlex instance.",
|
||||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||||
"name": "Instance name.",
|
"name": "Instance name.",
|
||||||
"backup_schedule": "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.",
|
"backup_schedule": "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.",
|
||||||
"retention_days": "The days of the retention period.",
|
"retention_days": "The days of the retention period.",
|
||||||
"flavor": "The block that defines the flavor data.",
|
"flavor_id": "The ID of the flavor.",
|
||||||
"flavor_id": "The ID of the flavor.",
|
"replicas": "The number of replicas.",
|
||||||
"flavor_description": "The flavor detailed flavor name.",
|
"storage": "The block of the storage configuration.",
|
||||||
"flavor_cpu": "The CPU count of the flavor.",
|
"storage_class": "The storage class used.",
|
||||||
"flavor_ram": "The RAM count of the flavor.",
|
"storage_size": "The disk size of the storage.",
|
||||||
"flavor_node_type": "The node type of the flavor. (Single or Replicas)",
|
"region": "The resource region. If not defined, the provider region is used.",
|
||||||
"replicas": "The number of replicas.",
|
"version": "The database version used.",
|
||||||
"storage": "The block of the storage configuration.",
|
"encryption": "The encryption block.",
|
||||||
"storage_class": "The storage class used.",
|
"keyring_id": "KeyRing ID of the encryption key.",
|
||||||
"storage_size": "The disk size of the storage.",
|
"key_id": "Key ID of the encryption key.",
|
||||||
"region": "The resource region. If not defined, the provider region is used.",
|
"key_version": "Key version of the encryption key.",
|
||||||
"version": "The database version used.",
|
"service_account": "The service account ID of the service account.",
|
||||||
"encryption": "The encryption block.",
|
"network": "The network block configuration.",
|
||||||
"keyring_id": "KeyRing ID of the encryption key.",
|
"access_scope": "The access scope. (Either SNA or PUBLIC)",
|
||||||
"key_id": "Key ID of the encryption key.",
|
"acl": "The Access Control List (ACL) for the PostgresFlex instance.",
|
||||||
"key_version": "Key version of the encryption key.",
|
"instance_address": "The returned instance address.",
|
||||||
"service_account": "The service account ID of the service account.",
|
"router_address": "The returned router address.",
|
||||||
"network": "The network block configuration.",
|
|
||||||
"access_scope": "The access scope. (Either SNA or PUBLIC)",
|
|
||||||
"acl": "The Access Control List (ACL) for the PostgresFlex instance.",
|
|
||||||
"instance_address": "The returned instance address.",
|
|
||||||
"router_address": "The returned router address.",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Schema = schema.Schema{
|
resp.Schema = schema.Schema{
|
||||||
|
|
@ -210,45 +202,8 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
|
||||||
Description: descriptions["retention_days"],
|
Description: descriptions["retention_days"],
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"flavor": schema.SingleNestedAttribute{
|
"flavor_id": schema.StringAttribute{
|
||||||
Required: true,
|
Required: true,
|
||||||
Description: descriptions["flavor"],
|
|
||||||
Attributes: map[string]schema.Attribute{
|
|
||||||
"id": schema.StringAttribute{
|
|
||||||
Description: descriptions["flavor_id"],
|
|
||||||
Computed: true,
|
|
||||||
Optional: true,
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
UseStateForUnknownIfFlavorUnchanged(req),
|
|
||||||
stringplanmodifier.RequiresReplace(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"description": schema.StringAttribute{
|
|
||||||
Computed: true,
|
|
||||||
Description: descriptions["flavor_description"],
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
UseStateForUnknownIfFlavorUnchanged(req),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"cpu": schema.Int64Attribute{
|
|
||||||
Description: descriptions["flavor_cpu"],
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
"ram": schema.Int64Attribute{
|
|
||||||
Description: descriptions["flavor_ram"],
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
"node_type": schema.StringAttribute{
|
|
||||||
Description: descriptions["flavor_node_type"],
|
|
||||||
Computed: true,
|
|
||||||
Optional: true,
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
// TODO @mhenselin anschauen
|
|
||||||
UseStateForUnknownIfFlavorUnchanged(req),
|
|
||||||
stringplanmodifier.RequiresReplace(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"replicas": schema.Int64Attribute{
|
"replicas": schema.Int64Attribute{
|
||||||
Required: true,
|
Required: true,
|
||||||
|
|
@ -390,15 +345,21 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
|
//func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
|
||||||
resp.IdentitySchema = identityschema.Schema{
|
// resp.IdentitySchema = identityschema.Schema{
|
||||||
Attributes: map[string]identityschema.Attribute{
|
// Attributes: map[string]identityschema.Attribute{
|
||||||
"id": identityschema.StringAttribute{
|
// "project_id": identityschema.StringAttribute{
|
||||||
RequiredForImport: true, // must be set during import by the practitioner
|
// RequiredForImport: true, // must be set during import by the practitioner
|
||||||
},
|
// },
|
||||||
},
|
// "region": identityschema.StringAttribute{
|
||||||
}
|
// RequiredForImport: true, // must be set during import by the practitioner
|
||||||
}
|
// },
|
||||||
|
// "instance_id": identityschema.StringAttribute{
|
||||||
|
// RequiredForImport: true, // must be set during import by the practitioner
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
// Create creates the resource and sets the initial Terraform state.
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
func (r *instanceResource) Create(
|
func (r *instanceResource) Create(
|
||||||
|
|
@ -406,7 +367,6 @@ func (r *instanceResource) Create(
|
||||||
req resource.CreateRequest,
|
req resource.CreateRequest,
|
||||||
resp *resource.CreateResponse,
|
resp *resource.CreateResponse,
|
||||||
) { // nolint:gocritic // function signature required by Terraform
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.Plan.Get(ctx, &model)
|
diags := req.Plan.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -430,42 +390,6 @@ func (r *instanceResource) Create(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := loadFlavorId(ctx, r.client, &model, flavor, storage)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading flavor ID: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flavor.Id.IsNull() || flavor.Id.IsUnknown() {
|
|
||||||
err := loadFlavorId(ctx, r.client, &model, flavor, storage)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
flavorValues := map[string]attr.Value{
|
|
||||||
"id": flavor.Id,
|
|
||||||
"description": flavor.Description,
|
|
||||||
"cpu": flavor.CPU,
|
|
||||||
"ram": flavor.RAM,
|
|
||||||
"node_type": flavor.NodeType,
|
|
||||||
}
|
|
||||||
var flavorObject basetypes.ObjectValue
|
|
||||||
flavorObject, diags = types.ObjectValue(flavorTypes, flavorValues)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if diags.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
model.Flavor = flavorObject
|
|
||||||
}
|
|
||||||
|
|
||||||
var encryption = &encryptionModel{}
|
var encryption = &encryptionModel{}
|
||||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||||
|
|
@ -494,7 +418,7 @@ func (r *instanceResource) Create(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate API request body from model
|
// Generate API request body from model
|
||||||
payload, err := toCreatePayload(&model, flavor, storage, encryption, network)
|
payload, err := toCreatePayload(&model, storage, encryption, network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -516,11 +440,13 @@ func (r *instanceResource) Create(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data returned by API in identity
|
//// Set data returned by API in identity
|
||||||
identity := IdentityModel{
|
//identity := IdentityModel{
|
||||||
ID: utils.BuildInternalTerraformId(projectId, region, instanceId),
|
// InstanceId: types.StringValue(instanceId),
|
||||||
}
|
// Region: types.StringValue(region),
|
||||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
// ProjectId: types.StringValue(projectId),
|
||||||
|
//}
|
||||||
|
//resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||||
|
|
||||||
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
|
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -529,7 +455,7 @@ func (r *instanceResource) Create(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, waitResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, r.client, waitResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -552,12 +478,12 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read identity data
|
//// Read identity data
|
||||||
var identityData IdentityModel
|
//var identityData IdentityModel
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
//resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||||
if resp.Diagnostics.HasError() {
|
//if resp.Diagnostics.HasError() {
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
|
@ -568,35 +494,27 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
var storage = storageModel{}
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var storage = &storageModel{}
|
|
||||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
diags = model.Storage.As(ctx, &storage, basetypes.ObjectAsOptions{})
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var network = &networkModel{}
|
var network = networkModel{}
|
||||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
diags = model.Network.As(ctx, &network, basetypes.ObjectAsOptions{})
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var encryption = &encryptionModel{}
|
var encryption = encryptionModel{}
|
||||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
diags = model.Encryption.As(ctx, &encryption, basetypes.ObjectAsOptions{})
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
|
|
@ -617,7 +535,7 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, r.client, instanceResp, &model, &storage, &encryption, &network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -629,18 +547,19 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
identityData.ID = model.Id
|
//identityData.InstanceId = model.InstanceId
|
||||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...)
|
//identityData.Region = model.Region
|
||||||
if resp.Diagnostics.HasError() {
|
//identityData.ProjectId = model.ProjectId
|
||||||
return
|
//resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...)
|
||||||
}
|
//if resp.Diagnostics.HasError() {
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
tflog.Info(ctx, "Postgres Flex instance read")
|
tflog.Info(ctx, "Postgres Flex instance 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 *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.Plan.Get(ctx, &model)
|
diags := req.Plan.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -676,20 +595,6 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := loadFlavorId(ctx, r.client, &model, flavor, storage)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Loading flavor ID: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var network = &networkModel{}
|
var network = &networkModel{}
|
||||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||||
|
|
@ -709,7 +614,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate API request body from model
|
// Generate API request body from model
|
||||||
payload, err := toUpdatePayload(&model, flavor, storage, network)
|
payload, err := toUpdatePayload(&model, storage, network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -730,7 +635,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, waitResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, r.client, waitResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -745,7 +650,6 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
|
|
||||||
// Delete deletes the resource and removes the Terraform state on success.
|
// Delete deletes the resource and removes the Terraform state on success.
|
||||||
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from state
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.State.Get(ctx, &model)
|
diags := req.State.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -771,11 +675,22 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).SetTimeout(45 * time.Minute).WaitWithContext(ctx)
|
//_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).SetTimeout(45 * time.Minute).WaitWithContext(ctx)
|
||||||
|
//if err != nil {
|
||||||
|
// core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
_, err = r.client.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
|
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
|
||||||
return
|
if ok && oapiErr.StatusCode != http.StatusNotFound {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp.State.RemoveResource(ctx)
|
||||||
tflog.Info(ctx, "Postgres Flex instance deleted")
|
tflog.Info(ctx, "Postgres Flex instance deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ func TestMapFields(t *testing.T) {
|
||||||
description string
|
description string
|
||||||
state Model
|
state Model
|
||||||
input *postgresflex.GetInstanceResponse
|
input *postgresflex.GetInstanceResponse
|
||||||
flavor *flavorModel
|
|
||||||
storage *storageModel
|
storage *storageModel
|
||||||
encryption *encryptionModel
|
encryption *encryptionModel
|
||||||
network *networkModel
|
network *networkModel
|
||||||
|
|
@ -51,9 +50,6 @@ func TestMapFields(t *testing.T) {
|
||||||
FlavorId: utils.Ptr("flavor_id"),
|
FlavorId: utils.Ptr("flavor_id"),
|
||||||
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(1))),
|
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(1))),
|
||||||
},
|
},
|
||||||
&flavorModel{
|
|
||||||
NodeType: types.StringValue("Single"),
|
|
||||||
},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{
|
&networkModel{
|
||||||
|
|
@ -67,16 +63,10 @@ func TestMapFields(t *testing.T) {
|
||||||
InstanceId: types.StringValue("iid"),
|
InstanceId: types.StringValue("iid"),
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
Name: types.StringNull(),
|
Name: types.StringNull(),
|
||||||
|
FlavorId: types.StringValue("flavor_id"),
|
||||||
//ACL: types.ListNull(types.StringType),
|
//ACL: types.ListNull(types.StringType),
|
||||||
BackupSchedule: types.StringNull(),
|
BackupSchedule: types.StringNull(),
|
||||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
Replicas: types.Int64Value(1),
|
||||||
"id": types.StringNull(),
|
|
||||||
"description": types.StringNull(),
|
|
||||||
"cpu": types.Int64Null(),
|
|
||||||
"ram": types.Int64Null(),
|
|
||||||
"node_type": types.StringValue("Single"),
|
|
||||||
}),
|
|
||||||
Replicas: types.Int64Value(1),
|
|
||||||
Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
|
Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
|
||||||
"keyring_id": types.StringNull(),
|
"keyring_id": types.StringNull(),
|
||||||
"key_id": types.StringNull(),
|
"key_id": types.StringNull(),
|
||||||
|
|
@ -171,7 +161,6 @@ func TestMapFields(t *testing.T) {
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
&flavorModel{},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -186,7 +175,6 @@ func TestMapFields(t *testing.T) {
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
},
|
},
|
||||||
&postgresflex.GetInstanceResponse{},
|
&postgresflex.GetInstanceResponse{},
|
||||||
&flavorModel{},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -197,7 +185,21 @@ func TestMapFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
err := mapFields(context.Background(), tt.input, &tt.state, tt.flavor, tt.storage, tt.encryption, tt.network, tt.region)
|
client := postgresFlexClientMocked{
|
||||||
|
returnError: false,
|
||||||
|
firstItem: 0,
|
||||||
|
lastItem: 0,
|
||||||
|
}
|
||||||
|
err := mapFields(
|
||||||
|
context.Background(),
|
||||||
|
client,
|
||||||
|
tt.input,
|
||||||
|
&tt.state,
|
||||||
|
tt.storage,
|
||||||
|
tt.encryption,
|
||||||
|
tt.network,
|
||||||
|
tt.region,
|
||||||
|
)
|
||||||
if !tt.isValid && err == nil {
|
if !tt.isValid && err == nil {
|
||||||
t.Fatalf("Should have failed")
|
t.Fatalf("Should have failed")
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +221,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
description string
|
description string
|
||||||
input *Model
|
input *Model
|
||||||
inputAcl []string
|
inputAcl []string
|
||||||
inputFlavor *flavorModel
|
|
||||||
inputStorage *storageModel
|
inputStorage *storageModel
|
||||||
inputEncryption *encryptionModel
|
inputEncryption *encryptionModel
|
||||||
inputNetwork *networkModel
|
inputNetwork *networkModel
|
||||||
|
|
@ -232,34 +233,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
Replicas: types.Int64Value(1),
|
Replicas: types.Int64Value(1),
|
||||||
},
|
},
|
||||||
[]string{},
|
[]string{},
|
||||||
&flavorModel{},
|
|
||||||
&storageModel{},
|
|
||||||
&encryptionModel{},
|
|
||||||
&networkModel{
|
|
||||||
ACL: types.ListValueMust(types.StringType, []attr.Value{
|
|
||||||
types.StringValue("0.0.0.0/0"),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
&postgresflex.CreateInstanceRequestPayload{
|
|
||||||
Acl: &[]string{"0.0.0.0/0"},
|
|
||||||
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}),
|
|
||||||
Encryption: &postgresflex.InstanceEncryption{},
|
|
||||||
Network: &postgresflex.InstanceNetwork{
|
|
||||||
Acl: &[]string{"0.0.0.0/0"},
|
|
||||||
},
|
|
||||||
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(1))),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"use flavor node_type instead of replicas",
|
|
||||||
&Model{},
|
|
||||||
[]string{
|
|
||||||
"0.0.0.0/0",
|
|
||||||
},
|
|
||||||
&flavorModel{
|
|
||||||
NodeType: types.StringValue("Single"),
|
|
||||||
},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{
|
&networkModel{
|
||||||
|
|
@ -282,7 +255,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
"nil_model",
|
"nil_model",
|
||||||
nil,
|
nil,
|
||||||
[]string{},
|
[]string{},
|
||||||
&flavorModel{},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -293,7 +265,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
"nil_acl",
|
"nil_acl",
|
||||||
&Model{},
|
&Model{},
|
||||||
nil,
|
nil,
|
||||||
&flavorModel{},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -304,7 +275,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
"nil_flavor",
|
"nil_flavor",
|
||||||
&Model{},
|
&Model{},
|
||||||
[]string{},
|
[]string{},
|
||||||
nil,
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -315,7 +285,6 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
"nil_storage",
|
"nil_storage",
|
||||||
&Model{},
|
&Model{},
|
||||||
[]string{},
|
[]string{},
|
||||||
&flavorModel{},
|
|
||||||
nil,
|
nil,
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{},
|
&networkModel{},
|
||||||
|
|
@ -325,7 +294,7 @@ func TestToCreatePayload(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
output, err := toCreatePayload(tt.input, tt.inputFlavor, tt.inputStorage, tt.inputEncryption, tt.inputNetwork)
|
output, err := toCreatePayload(tt.input, tt.inputStorage, tt.inputEncryption, tt.inputNetwork)
|
||||||
if !tt.isValid && err == nil {
|
if !tt.isValid && err == nil {
|
||||||
t.Fatalf("Should have failed")
|
t.Fatalf("Should have failed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
package postgresflexalpha
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type useStateForUnknownIfFlavorUnchangedModifier struct {
|
|
||||||
Req resource.SchemaRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown
|
|
||||||
// if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing.
|
|
||||||
func UseStateForUnknownIfFlavorUnchanged(req resource.SchemaRequest) planmodifier.String {
|
|
||||||
return useStateForUnknownIfFlavorUnchangedModifier{
|
|
||||||
Req: req,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m useStateForUnknownIfFlavorUnchangedModifier) Description(context.Context) string {
|
|
||||||
return "UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing."
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m useStateForUnknownIfFlavorUnchangedModifier) MarkdownDescription(ctx context.Context) string {
|
|
||||||
return m.Description(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m useStateForUnknownIfFlavorUnchangedModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { // nolint:gocritic // function signature required by Terraform
|
|
||||||
// Do nothing if there is no state value.
|
|
||||||
if req.StateValue.IsNull() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do nothing if there is a known planned value.
|
|
||||||
if !req.PlanValue.IsUnknown() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up.
|
|
||||||
if req.ConfigValue.IsUnknown() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The above checks are taken from the UseStateForUnknown plan modifier implementation
|
|
||||||
// (https://github.com/hashicorp/terraform-plugin-framework/blob/main/resource/schema/stringplanmodifier/use_state_for_unknown.go#L38)
|
|
||||||
|
|
||||||
var stateModel Model
|
|
||||||
diags := req.State.Get(ctx, &stateModel)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateFlavor = &flavorModel{}
|
|
||||||
if !stateModel.Flavor.IsNull() && !stateModel.Flavor.IsUnknown() {
|
|
||||||
diags = stateModel.Flavor.As(ctx, stateFlavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var planModel Model
|
|
||||||
diags = req.Plan.Get(ctx, &planModel)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var planFlavor = &flavorModel{}
|
|
||||||
if !planModel.Flavor.IsNull() && !planModel.Flavor.IsUnknown() {
|
|
||||||
diags = planModel.Flavor.As(ctx, planFlavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if planFlavor.CPU == stateFlavor.CPU && planFlavor.RAM == stateFlavor.RAM {
|
|
||||||
resp.PlanValue = req.StateValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
|
||||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Instance resource data
|
// Instance resource data
|
||||||
|
|
@ -364,10 +363,10 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("deleting instance %s during CheckDestroy: %w", *items[i].Id, err)
|
return fmt.Errorf("deleting instance %s during CheckDestroy: %w", *items[i].Id, err)
|
||||||
}
|
}
|
||||||
_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Id).WaitWithContext(ctx)
|
//_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, *items[i].Id).WaitWithContext(ctx)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
return fmt.Errorf("deleting instance %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err)
|
// return fmt.Errorf("deleting instance %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err)
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -41,16 +41,16 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
Id types.String `tfsdk:"id"` // needed by TF
|
Id types.String `tfsdk:"id"` // needed by TF
|
||||||
UserId types.Int64 `tfsdk:"user_id"`
|
UserId types.Int64 `tfsdk:"user_id"`
|
||||||
InstanceId types.String `tfsdk:"instance_id"`
|
InstanceId types.String `tfsdk:"instance_id"`
|
||||||
ProjectId types.String `tfsdk:"project_id"`
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
Username types.String `tfsdk:"username"`
|
Username types.String `tfsdk:"username"`
|
||||||
Roles types.Set `tfsdk:"roles"`
|
Roles types.Set `tfsdk:"roles"`
|
||||||
Password types.String `tfsdk:"password"`
|
Password types.String `tfsdk:"password"`
|
||||||
Host types.String `tfsdk:"host"`
|
//Host types.String `tfsdk:"host"`
|
||||||
Port types.Int64 `tfsdk:"port"`
|
//Port types.Int64 `tfsdk:"port"`
|
||||||
Uri types.String `tfsdk:"uri"`
|
//Uri types.String `tfsdk:"uri"`
|
||||||
Region types.String `tfsdk:"region"`
|
Region types.String `tfsdk:"region"`
|
||||||
Status types.String `tfsdk:"status"`
|
Status types.String `tfsdk:"status"`
|
||||||
ConnectionString types.String `tfsdk:"connection_string"`
|
ConnectionString types.String `tfsdk:"connection_string"`
|
||||||
|
|
@ -124,6 +124,7 @@ func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequ
|
||||||
|
|
||||||
// Schema defines the schema for the resource.
|
// Schema defines the schema for the resource.
|
||||||
func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
|
// rolesOptions := []string{"login", "createdb", "createrole"}
|
||||||
rolesOptions := []string{"login", "createdb"}
|
rolesOptions := []string{"login", "createdb"}
|
||||||
|
|
||||||
descriptions := map[string]string{
|
descriptions := map[string]string{
|
||||||
|
|
@ -181,9 +182,9 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"username": schema.StringAttribute{
|
"username": schema.StringAttribute{
|
||||||
Required: true,
|
Required: true,
|
||||||
PlanModifiers: []planmodifier.String{
|
PlanModifiers: []planmodifier.String{
|
||||||
stringplanmodifier.RequiresReplace(),
|
// stringplanmodifier.RequiresReplace(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"roles": schema.SetAttribute{
|
"roles": schema.SetAttribute{
|
||||||
|
|
@ -192,7 +193,7 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
|
||||||
Required: true,
|
Required: true,
|
||||||
Validators: []validator.Set{
|
Validators: []validator.Set{
|
||||||
setvalidator.ValueStringsAre(
|
setvalidator.ValueStringsAre(
|
||||||
stringvalidator.OneOf("login", "createdb"),
|
stringvalidator.OneOf(rolesOptions...),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -200,16 +201,16 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
},
|
},
|
||||||
"host": schema.StringAttribute{
|
//"host": schema.StringAttribute{
|
||||||
Computed: true,
|
// Computed: true,
|
||||||
},
|
//},
|
||||||
"port": schema.Int64Attribute{
|
//"port": schema.Int64Attribute{
|
||||||
Computed: true,
|
// Computed: true,
|
||||||
},
|
//},
|
||||||
"uri": schema.StringAttribute{
|
//"uri": schema.StringAttribute{
|
||||||
Computed: true,
|
// Computed: true,
|
||||||
Sensitive: true,
|
// Sensitive: true,
|
||||||
},
|
//},
|
||||||
"region": schema.StringAttribute{
|
"region": schema.StringAttribute{
|
||||||
Optional: true,
|
Optional: true,
|
||||||
// must be computed to allow for storing the override value from the provider
|
// must be computed to allow for storing the override value from the provider
|
||||||
|
|
@ -375,7 +376,6 @@ func (r *userResource) Update(
|
||||||
req resource.UpdateRequest,
|
req resource.UpdateRequest,
|
||||||
resp *resource.UpdateResponse,
|
resp *resource.UpdateResponse,
|
||||||
) { // nolint:gocritic // function signature required by Terraform
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.Plan.Get(ctx, &model)
|
diags := req.Plan.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -466,7 +466,6 @@ func (r *userResource) Delete(
|
||||||
req resource.DeleteRequest,
|
req resource.DeleteRequest,
|
||||||
resp *resource.DeleteResponse,
|
resp *resource.DeleteResponse,
|
||||||
) { // nolint:gocritic // function signature required by Terraform
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.State.Get(ctx, &model)
|
diags := req.State.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -581,9 +580,10 @@ func mapFieldsCreate(
|
||||||
model.Roles = rolesSet
|
model.Roles = rolesSet
|
||||||
}
|
}
|
||||||
|
|
||||||
model.Password = types.StringValue(*user.Password)
|
model.Password = types.StringPointerValue(user.Password)
|
||||||
model.Region = types.StringValue(region)
|
model.Region = types.StringValue(region)
|
||||||
model.Status = types.StringPointerValue(user.Status)
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
|
//model.Host = types.StringPointerValue()
|
||||||
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
|
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -625,8 +625,8 @@ func mapFields(userResp *postgresflex.GetUserResponse, model *Model, region stri
|
||||||
}
|
}
|
||||||
model.Roles = rolesSet
|
model.Roles = rolesSet
|
||||||
}
|
}
|
||||||
model.Host = types.StringPointerValue(user.Host)
|
//model.Host = types.StringPointerValue(user.Host)
|
||||||
model.Port = types.Int64PointerValue(user.Port)
|
//model.Port = types.Int64PointerValue(user.Port)
|
||||||
model.Region = types.StringValue(region)
|
model.Region = types.StringValue(region)
|
||||||
model.Status = types.StringPointerValue(user.Status)
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
|
model.ConnectionString = types.StringPointerValue(user.ConnectionString)
|
||||||
|
|
@ -667,6 +667,7 @@ func toUpdatePayload(model *Model, roles *[]string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
return &postgresflex.UpdateUserRequestPayload{
|
return &postgresflex.UpdateUserRequestPayload{
|
||||||
|
Name: conversion.StringValueToPointer(model.Username),
|
||||||
Roles: toPayloadRoles(roles),
|
Roles: toPayloadRoles(roles),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
package sqlserverFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
|
||||||
|
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha"
|
||||||
|
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
var (
|
||||||
|
_ datasource.DataSource = &flavorDataSource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlavorModel struct {
|
||||||
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
|
Region types.String `tfsdk:"region"`
|
||||||
|
StorageClass types.String `tfsdk:"storage_class"`
|
||||||
|
Cpu types.Int64 `tfsdk:"cpu"`
|
||||||
|
Description types.String `tfsdk:"description"`
|
||||||
|
Id types.String `tfsdk:"id"`
|
||||||
|
FlavorId types.String `tfsdk:"flavor_id"`
|
||||||
|
MaxGb types.Int64 `tfsdk:"max_gb"`
|
||||||
|
Memory types.Int64 `tfsdk:"ram"`
|
||||||
|
MinGb types.Int64 `tfsdk:"min_gb"`
|
||||||
|
NodeType types.String `tfsdk:"node_type"`
|
||||||
|
StorageClasses types.List `tfsdk:"storage_classes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFlavorDataSource is a helper function to simplify the provider implementation.
|
||||||
|
func NewFlavorDataSource() datasource.DataSource {
|
||||||
|
return &flavorDataSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flavorDataSource is the data source implementation.
|
||||||
|
type flavorDataSource struct {
|
||||||
|
client *sqlserverflexalpha.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the data source type name.
|
||||||
|
func (r *flavorDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||||
|
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_flavor"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure adds the provider configured client to the data source.
|
||||||
|
func (r *flavorDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.client = apiClient
|
||||||
|
tflog.Info(ctx, "Postgres Flex instance client configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
|
resp.Schema = schema.Schema{
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"project_id": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The cpu count of the instance.",
|
||||||
|
MarkdownDescription: "The cpu count of the instance.",
|
||||||
|
},
|
||||||
|
"region": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The flavor description.",
|
||||||
|
MarkdownDescription: "The flavor description.",
|
||||||
|
},
|
||||||
|
"cpu": schema.Int64Attribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The cpu count of the instance.",
|
||||||
|
MarkdownDescription: "The cpu count of the instance.",
|
||||||
|
},
|
||||||
|
"ram": schema.Int64Attribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The memory of the instance in Gibibyte.",
|
||||||
|
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||||
|
},
|
||||||
|
"storage_class": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "The memory of the instance in Gibibyte.",
|
||||||
|
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||||
|
},
|
||||||
|
"description": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The flavor description.",
|
||||||
|
MarkdownDescription: "The flavor description.",
|
||||||
|
},
|
||||||
|
"id": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The terraform id of the instance flavor.",
|
||||||
|
MarkdownDescription: "The terraform id of the instance flavor.",
|
||||||
|
},
|
||||||
|
"flavor_id": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "The flavor id of the instance flavor.",
|
||||||
|
MarkdownDescription: "The flavor id of the instance flavor.",
|
||||||
|
},
|
||||||
|
"max_gb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||||
|
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||||
|
},
|
||||||
|
"min_gb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
Description: "minimum storage which is required to order in Gigabyte.",
|
||||||
|
MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
|
||||||
|
},
|
||||||
|
"node_type": schema.StringAttribute{
|
||||||
|
Required: true,
|
||||||
|
Description: "defines the nodeType it can be either single or replica",
|
||||||
|
MarkdownDescription: "defines the nodeType it can be either single or replica",
|
||||||
|
},
|
||||||
|
"storage_classes": schema.ListNestedAttribute{
|
||||||
|
Computed: true,
|
||||||
|
NestedObject: schema.NestedAttributeObject{
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"class": schema.StringAttribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"max_io_per_sec": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"max_through_in_mb": schema.Int64Attribute{
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomType: sqlserverflex.StorageClassesType{
|
||||||
|
ObjectType: types.ObjectType{
|
||||||
|
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
|
var model FlavorModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
flavors, err := getAllFlavors(ctx, r.client, projectId, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading flavors", fmt.Sprintf("getAllFlavors: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundFlavors []sqlserverflexalpha.ListFlavors
|
||||||
|
for _, flavor := range flavors {
|
||||||
|
if model.Cpu.ValueInt64() != *flavor.Cpu {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if model.Memory.ValueInt64() != *flavor.Memory {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if model.NodeType.ValueString() != *flavor.NodeType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, sc := range *flavor.StorageClasses {
|
||||||
|
if model.StorageClass.ValueString() != *sc.Class {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundFlavors = append(foundFlavors, flavor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(foundFlavors) == 0 {
|
||||||
|
resp.Diagnostics.AddError("get flavor", "could not find requested flavor")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(foundFlavors) > 1 {
|
||||||
|
resp.Diagnostics.AddError("get flavor", "found too many matching flavors")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f := foundFlavors[0]
|
||||||
|
model.Description = types.StringValue(*f.Description)
|
||||||
|
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, *f.Id)
|
||||||
|
model.FlavorId = types.StringValue(*f.Id)
|
||||||
|
model.MaxGb = types.Int64Value(*f.MaxGB)
|
||||||
|
model.MinGb = types.Int64Value(*f.MinGB)
|
||||||
|
|
||||||
|
if f.StorageClasses == nil {
|
||||||
|
model.StorageClasses = types.ListNull(sqlserverflex.StorageClassesType{
|
||||||
|
ObjectType: basetypes.ObjectType{
|
||||||
|
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
var scList []attr.Value
|
||||||
|
for _, sc := range *f.StorageClasses {
|
||||||
|
scList = append(
|
||||||
|
scList,
|
||||||
|
sqlserverflex.NewStorageClassesValueMust(
|
||||||
|
sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
map[string]attr.Value{
|
||||||
|
"class": types.StringValue(*sc.Class),
|
||||||
|
"max_io_per_sec": types.Int64Value(*sc.MaxIoPerSec),
|
||||||
|
"max_through_in_mb": types.Int64Value(*sc.MaxThroughInMb),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
storageClassesList := types.ListValueMust(
|
||||||
|
sqlserverflex.StorageClassesType{
|
||||||
|
ObjectType: basetypes.ObjectType{
|
||||||
|
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scList,
|
||||||
|
)
|
||||||
|
model.StorageClasses = storageClassesList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set refreshed state
|
||||||
|
diags = resp.State.Set(ctx, model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors read")
|
||||||
|
}
|
||||||
201
stackit/internal/services/sqlserverflexalpha/flavor/functions.go
Normal file
201
stackit/internal/services/sqlserverflexalpha/flavor/functions.go
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
package sqlserverFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flavorsClient interface {
|
||||||
|
GetFlavorsRequestExecute(
|
||||||
|
ctx context.Context,
|
||||||
|
projectId, region string,
|
||||||
|
page, size *int64,
|
||||||
|
sort *sqlserverflex.FlavorSort,
|
||||||
|
) (*sqlserverflex.GetFlavorsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
|
||||||
|
// if model == nil {
|
||||||
|
// return fmt.Errorf("nil model")
|
||||||
|
// }
|
||||||
|
// if flavor == nil {
|
||||||
|
// return fmt.Errorf("nil flavor")
|
||||||
|
// }
|
||||||
|
// cpu := flavor.CPU.ValueInt64()
|
||||||
|
// if cpu == 0 {
|
||||||
|
// return fmt.Errorf("nil CPU")
|
||||||
|
// }
|
||||||
|
// ram := flavor.RAM.ValueInt64()
|
||||||
|
// if ram == 0 {
|
||||||
|
// return fmt.Errorf("nil RAM")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nodeType := flavor.NodeType.ValueString()
|
||||||
|
// if nodeType == "" {
|
||||||
|
// if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
|
||||||
|
// return fmt.Errorf("nil NodeType")
|
||||||
|
// }
|
||||||
|
// switch model.Replicas.ValueInt64() {
|
||||||
|
// case 1:
|
||||||
|
// nodeType = "Single"
|
||||||
|
// case 3:
|
||||||
|
// nodeType = "Replica"
|
||||||
|
// default:
|
||||||
|
// return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// storageClass := conversion.StringValueToPointer(storage.Class)
|
||||||
|
// if storageClass == nil {
|
||||||
|
// return fmt.Errorf("nil StorageClass")
|
||||||
|
// }
|
||||||
|
// storageSize := conversion.Int64ValueToPointer(storage.Size)
|
||||||
|
// if storageSize == nil {
|
||||||
|
// return fmt.Errorf("nil StorageSize")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// projectId := model.ProjectId.ValueString()
|
||||||
|
// region := model.Region.ValueString()
|
||||||
|
//
|
||||||
|
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// avl := ""
|
||||||
|
// foundFlavorCount := 0
|
||||||
|
// var foundFlavors []string
|
||||||
|
// for _, f := range flavorList {
|
||||||
|
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if !strings.EqualFold(*f.NodeType, nodeType) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *f.Cpu == cpu && *f.Memory == ram {
|
||||||
|
// var useSc *sqlserverflex.FlavorStorageClassesStorageClass
|
||||||
|
// for _, sc := range *f.StorageClasses {
|
||||||
|
// if *sc.Class != *storageClass {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
|
||||||
|
// return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
|
||||||
|
// }
|
||||||
|
// useSc = &sc
|
||||||
|
// }
|
||||||
|
// if useSc == nil {
|
||||||
|
// return fmt.Errorf("no storage class found for %s", *storageClass)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// flavor.Id = types.StringValue(*f.Id)
|
||||||
|
// flavor.Description = types.StringValue(*f.Description)
|
||||||
|
// foundFlavors = append(foundFlavors, fmt.Sprintf("%s (%d/%d - %s)", *f.Id, *f.Cpu, *f.Memory, *f.NodeType))
|
||||||
|
// foundFlavorCount++
|
||||||
|
// }
|
||||||
|
// for _, cls := range *f.StorageClasses {
|
||||||
|
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if foundFlavorCount > 1 {
|
||||||
|
// return fmt.Errorf(
|
||||||
|
// "number of flavors returned: %d\nmultiple flavors found: %d flavors\n %s",
|
||||||
|
// len(flavorList),
|
||||||
|
// foundFlavorCount,
|
||||||
|
// strings.Join(foundFlavors, "\n "),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// if flavor.Id.ValueString() == "" {
|
||||||
|
// return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
||||||
|
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) ([]sqlserverflex.ListFlavors, error) {
|
||||||
|
if projectId == "" || region == "" {
|
||||||
|
return nil, fmt.Errorf("listing sqlserverflex flavors: projectId and region are required")
|
||||||
|
}
|
||||||
|
var flavorList []sqlserverflex.ListFlavors
|
||||||
|
|
||||||
|
page := int64(1)
|
||||||
|
size := int64(10)
|
||||||
|
sort := sqlserverflex.FLAVORSORT_INDEX_ASC
|
||||||
|
counter := 0
|
||||||
|
for {
|
||||||
|
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing sqlserverflex flavors: %w", err)
|
||||||
|
}
|
||||||
|
if res.Flavors == nil {
|
||||||
|
return nil, fmt.Errorf("finding flavors for project %s", projectId)
|
||||||
|
}
|
||||||
|
pagination := res.GetPagination()
|
||||||
|
flavors := res.GetFlavors()
|
||||||
|
flavorList = append(flavorList, flavors...)
|
||||||
|
|
||||||
|
if *pagination.TotalRows < int64(len(flavorList)) {
|
||||||
|
return nil, fmt.Errorf("total rows is smaller than current accumulated list - that should not happen")
|
||||||
|
}
|
||||||
|
if *pagination.TotalRows == int64(len(flavorList)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
|
||||||
|
if page > *pagination.TotalPages {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement a breakpoint
|
||||||
|
counter++
|
||||||
|
if counter > 1000 {
|
||||||
|
panic("too many pagination results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flavorList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
|
||||||
|
// if model == nil {
|
||||||
|
// return fmt.Errorf("nil model")
|
||||||
|
// }
|
||||||
|
// if flavor == nil {
|
||||||
|
// return fmt.Errorf("nil flavor")
|
||||||
|
// }
|
||||||
|
// id := conversion.StringValueToPointer(flavor.Id)
|
||||||
|
// if id == nil {
|
||||||
|
// return fmt.Errorf("nil flavor ID")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// flavor.Id = types.StringValue("")
|
||||||
|
//
|
||||||
|
// projectId := model.ProjectId.ValueString()
|
||||||
|
// region := model.Region.ValueString()
|
||||||
|
//
|
||||||
|
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// avl := ""
|
||||||
|
// for _, f := range flavorList {
|
||||||
|
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if *f.Id == *id {
|
||||||
|
// flavor.Id = types.StringValue(*f.Id)
|
||||||
|
// flavor.Description = types.StringValue(*f.Description)
|
||||||
|
// flavor.CPU = types.Int64Value(*f.Cpu)
|
||||||
|
// flavor.RAM = types.Int64Value(*f.Memory)
|
||||||
|
// flavor.NodeType = types.StringValue(*f.NodeType)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||||
|
// }
|
||||||
|
// if flavor.Id.ValueString() == "" {
|
||||||
|
// return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package postgresFlexAlphaFlavor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
|
||||||
|
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
var (
|
||||||
|
_ datasource.DataSource = &flavorListDataSource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFlavorListDataSource is a helper function to simplify the provider implementation.
|
||||||
|
func NewFlavorListDataSource() datasource.DataSource {
|
||||||
|
return &flavorListDataSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flavorDataSource is the data source implementation.
|
||||||
|
type flavorListDataSource struct {
|
||||||
|
client *postgresflexalpha.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata returns the data source type name.
|
||||||
|
func (r *flavorListDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||||
|
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavorlist"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure adds the provider configured client to the data source.
|
||||||
|
func (r *flavorListDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.client = apiClient
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors client configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorListDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
|
resp.Schema = postgresflex.FlavorDataSourceSchema(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *flavorListDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
|
var model postgresflex.FlavorModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
// Set refreshed state
|
||||||
|
diags = resp.State.Set(ctx, model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "Postgres Flex flavors read")
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -253,22 +253,6 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
|
||||||
if model.Flavor.IsNull() || model.Flavor.IsUnknown() {
|
|
||||||
flavor.Id = types.StringValue(*instanceResp.FlavorId)
|
|
||||||
if flavor.Id.IsNull() || flavor.Id.IsUnknown() || flavor.Id.String() == "" {
|
|
||||||
panic("WTF FlavorId can not be null or empty string")
|
|
||||||
}
|
|
||||||
err = getFlavorModelById(ctx, r.client, &model, flavor)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if flavor.CPU.IsNull() || flavor.CPU.IsUnknown() || flavor.CPU.String() == "" {
|
|
||||||
panic("WTF FlavorId can not be null or empty string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage = &storageModel{}
|
var storage = &storageModel{}
|
||||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||||
|
|
@ -296,7 +280,7 @@ func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
|
@ -19,7 +17,7 @@ type sqlserverflexClient interface {
|
||||||
GetFlavorsRequestExecute(ctx context.Context, projectId, region string, page, size *int64, sort *sqlserverflex.FlavorSort) (*sqlserverflex.GetFlavorsResponse, error)
|
GetFlavorsRequestExecute(ctx context.Context, projectId, region string, page, size *int64, sort *sqlserverflex.FlavorSort) (*sqlserverflex.GetFlavorsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, model *Model, flavor *flavorModel, storage *storageModel, encryption *encryptionModel, network *networkModel, region string) error {
|
func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, model *Model, storage *storageModel, encryption *encryptionModel, network *networkModel, region string) error {
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
return fmt.Errorf("response input is nil")
|
return fmt.Errorf("response input is nil")
|
||||||
}
|
}
|
||||||
|
|
@ -37,26 +35,6 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod
|
||||||
return fmt.Errorf("instance id not present")
|
return fmt.Errorf("instance id not present")
|
||||||
}
|
}
|
||||||
|
|
||||||
var flavorValues map[string]attr.Value
|
|
||||||
if instance.FlavorId == nil {
|
|
||||||
return fmt.Errorf("instance has no flavor id")
|
|
||||||
}
|
|
||||||
if *instance.FlavorId != flavor.Id.ValueString() {
|
|
||||||
return fmt.Errorf("instance has different flavor id %s - %s", *instance.FlavorId, flavor.Id.ValueString())
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorValues = map[string]attr.Value{
|
|
||||||
"id": flavor.Id,
|
|
||||||
"description": flavor.Description,
|
|
||||||
"cpu": flavor.CPU,
|
|
||||||
"ram": flavor.RAM,
|
|
||||||
"node_type": flavor.NodeType,
|
|
||||||
}
|
|
||||||
flavorObject, diags := types.ObjectValue(flavorTypes, flavorValues)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("creating flavor: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
|
|
||||||
var storageValues map[string]attr.Value
|
var storageValues map[string]attr.Value
|
||||||
if instance.Storage == nil {
|
if instance.Storage == nil {
|
||||||
storageValues = map[string]attr.Value{
|
storageValues = map[string]attr.Value{
|
||||||
|
|
@ -163,7 +141,7 @@ func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, mod
|
||||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
|
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
|
||||||
model.InstanceId = types.StringValue(instanceId)
|
model.InstanceId = types.StringValue(instanceId)
|
||||||
model.Name = types.StringPointerValue(instance.Name)
|
model.Name = types.StringPointerValue(instance.Name)
|
||||||
model.Flavor = flavorObject
|
model.FlavorId = types.StringPointerValue(instance.FlavorId)
|
||||||
model.Replicas = types.Int64Value(int64(*instance.Replicas))
|
model.Replicas = types.Int64Value(int64(*instance.Replicas))
|
||||||
model.Storage = storageObject
|
model.Storage = storageObject
|
||||||
model.Version = types.StringValue(string(*instance.Version))
|
model.Version = types.StringValue(string(*instance.Version))
|
||||||
|
|
@ -182,10 +160,6 @@ func toCreatePayload(model *Model, storage *storageModel, encryption *encryption
|
||||||
return nil, fmt.Errorf("nil model")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.Flavor.IsNull() || model.Flavor.IsUnknown() {
|
|
||||||
return nil, fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
|
|
||||||
storagePayload := &sqlserverflex.CreateInstanceRequestPayloadGetStorageArgType{}
|
storagePayload := &sqlserverflex.CreateInstanceRequestPayloadGetStorageArgType{}
|
||||||
if storage != nil {
|
if storage != nil {
|
||||||
storagePayload.Class = conversion.StringValueToPointer(storage.Class)
|
storagePayload.Class = conversion.StringValueToPointer(storage.Class)
|
||||||
|
|
@ -200,16 +174,6 @@ func toCreatePayload(model *Model, storage *storageModel, encryption *encryption
|
||||||
encryptionPayload.ServiceAccount = conversion.StringValueToPointer(encryption.ServiceAccount)
|
encryptionPayload.ServiceAccount = conversion.StringValueToPointer(encryption.ServiceAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorId := ""
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
modelValues := model.Flavor.Attributes()
|
|
||||||
if _, ok := modelValues["id"]; !ok {
|
|
||||||
return nil, fmt.Errorf("flavor has not yet been created")
|
|
||||||
}
|
|
||||||
// TODO - how to get rid of that trim?
|
|
||||||
flavorId = strings.Trim(modelValues["id"].String(), "\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
var aclElements []string
|
var aclElements []string
|
||||||
if network != nil && !network.ACL.IsNull() && !network.ACL.IsUnknown() {
|
if network != nil && !network.ACL.IsNull() && !network.ACL.IsUnknown() {
|
||||||
aclElements = make([]string, 0, len(network.ACL.Elements()))
|
aclElements = make([]string, 0, len(network.ACL.Elements()))
|
||||||
|
|
@ -227,31 +191,20 @@ func toCreatePayload(model *Model, storage *storageModel, encryption *encryption
|
||||||
|
|
||||||
return &sqlserverflex.CreateInstanceRequestPayload{
|
return &sqlserverflex.CreateInstanceRequestPayload{
|
||||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||||
FlavorId: &flavorId,
|
Encryption: encryptionPayload,
|
||||||
|
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||||
Name: conversion.StringValueToPointer(model.Name),
|
Name: conversion.StringValueToPointer(model.Name),
|
||||||
|
Network: networkPayload,
|
||||||
|
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
|
||||||
Storage: storagePayload,
|
Storage: storagePayload,
|
||||||
Version: sqlserverflex.CreateInstanceRequestPayloadGetVersionAttributeType(conversion.StringValueToPointer(model.Version)),
|
Version: sqlserverflex.CreateInstanceRequestPayloadGetVersionAttributeType(conversion.StringValueToPointer(model.Version)),
|
||||||
Encryption: encryptionPayload,
|
|
||||||
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
|
|
||||||
Network: networkPayload,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toUpdatePayload(model *Model, storage *storageModel, network *networkModel) (*sqlserverflex.UpdateInstancePartiallyRequestPayload, error) {
|
func toUpdatePartiallyPayload(model *Model, storage *storageModel, network *networkModel) (*sqlserverflex.UpdateInstancePartiallyRequestPayload, error) {
|
||||||
if model == nil {
|
if model == nil {
|
||||||
return nil, fmt.Errorf("nil model")
|
return nil, fmt.Errorf("nil model")
|
||||||
}
|
}
|
||||||
if model.Flavor.IsNull() || model.Flavor.IsUnknown() {
|
|
||||||
return nil, fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
var flavorMdl flavorModel
|
|
||||||
diag := model.Flavor.As(context.Background(), &flavorMdl, basetypes.ObjectAsOptions{
|
|
||||||
UnhandledNullAsEmpty: true,
|
|
||||||
UnhandledUnknownAsEmpty: false,
|
|
||||||
})
|
|
||||||
if diag.HasError() {
|
|
||||||
return nil, fmt.Errorf("flavor conversion error: %v", diag.Errors())
|
|
||||||
}
|
|
||||||
|
|
||||||
storagePayload := &sqlserverflex.UpdateInstanceRequestPayloadGetStorageArgType{}
|
storagePayload := &sqlserverflex.UpdateInstanceRequestPayloadGetStorageArgType{}
|
||||||
if storage != nil {
|
if storage != nil {
|
||||||
|
|
@ -277,12 +230,11 @@ func toUpdatePayload(model *Model, storage *storageModel, network *networkModel)
|
||||||
return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64())
|
return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64())
|
||||||
}
|
}
|
||||||
replCount := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
|
replCount := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
|
||||||
flavorId := flavorMdl.Id.ValueString()
|
|
||||||
return &sqlserverflex.UpdateInstancePartiallyRequestPayload{
|
return &sqlserverflex.UpdateInstancePartiallyRequestPayload{
|
||||||
Network: networkPayload,
|
|
||||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||||
FlavorId: &flavorId,
|
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||||
Name: conversion.StringValueToPointer(model.Name),
|
Name: conversion.StringValueToPointer(model.Name),
|
||||||
|
Network: networkPayload,
|
||||||
Replicas: sqlserverflex.UpdateInstancePartiallyRequestPayloadGetReplicasAttributeType(&replCount),
|
Replicas: sqlserverflex.UpdateInstancePartiallyRequestPayloadGetReplicasAttributeType(&replCount),
|
||||||
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
|
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
|
||||||
Storage: storagePayload,
|
Storage: storagePayload,
|
||||||
|
|
@ -290,152 +242,15 @@ func toUpdatePayload(model *Model, storage *storageModel, network *networkModel)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllFlavors(ctx context.Context, client sqlserverflexClient, projectId, region string) ([]sqlserverflex.ListFlavors, error) {
|
func toUpdatePayload(model *Model, storage *storageModel, network *networkModel) (*sqlserverflex.UpdateInstanceRequestPayload, error) {
|
||||||
if projectId == "" || region == "" {
|
return &sqlserverflex.UpdateInstanceRequestPayload{
|
||||||
return nil, fmt.Errorf("listing sqlserverflex flavors: projectId and region are required")
|
BackupSchedule: nil,
|
||||||
}
|
FlavorId: nil,
|
||||||
var flavorList []sqlserverflex.ListFlavors
|
Name: nil,
|
||||||
|
Network: nil,
|
||||||
page := int64(1)
|
Replicas: nil,
|
||||||
size := int64(10)
|
RetentionDays: nil,
|
||||||
for {
|
Storage: nil,
|
||||||
sort := sqlserverflex.FLAVORSORT_INDEX_ASC
|
Version: nil,
|
||||||
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
|
}, nil
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("listing sqlserverflex flavors: %w", err)
|
|
||||||
}
|
|
||||||
if res.Flavors == nil {
|
|
||||||
return nil, fmt.Errorf("finding flavors for project %s", projectId)
|
|
||||||
}
|
|
||||||
pagination := res.GetPagination()
|
|
||||||
flavorList = append(flavorList, *res.Flavors...)
|
|
||||||
|
|
||||||
if *pagination.TotalRows == int64(len(flavorList)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
page++
|
|
||||||
}
|
|
||||||
return flavorList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFlavorId(ctx context.Context, client sqlserverflexClient, model *Model, flavor *flavorModel, storage *storageModel) error {
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
if flavor == nil {
|
|
||||||
return fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
cpu := conversion.Int64ValueToPointer(flavor.CPU)
|
|
||||||
if cpu == nil {
|
|
||||||
return fmt.Errorf("nil CPU")
|
|
||||||
}
|
|
||||||
ram := conversion.Int64ValueToPointer(flavor.RAM)
|
|
||||||
if ram == nil {
|
|
||||||
return fmt.Errorf("nil RAM")
|
|
||||||
}
|
|
||||||
nodeType := conversion.StringValueToPointer(flavor.NodeType)
|
|
||||||
if nodeType == nil {
|
|
||||||
return fmt.Errorf("nil NodeType")
|
|
||||||
}
|
|
||||||
storageClass := conversion.StringValueToPointer(storage.Class)
|
|
||||||
if storageClass == nil {
|
|
||||||
return fmt.Errorf("nil StorageClass")
|
|
||||||
}
|
|
||||||
storageSize := conversion.Int64ValueToPointer(storage.Size)
|
|
||||||
if storageSize == nil {
|
|
||||||
return fmt.Errorf("nil StorageSize")
|
|
||||||
}
|
|
||||||
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
region := model.Region.ValueString()
|
|
||||||
|
|
||||||
flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
avl := ""
|
|
||||||
foundFlavorCount := 0
|
|
||||||
for _, f := range flavorList {
|
|
||||||
if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(*f.NodeType, *nodeType) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *f.Cpu == *cpu && *f.Memory == *ram {
|
|
||||||
var useSc *sqlserverflex.FlavorStorageClassesStorageClass
|
|
||||||
for _, sc := range *f.StorageClasses {
|
|
||||||
if *sc.Class != *storageClass {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
|
|
||||||
return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
|
|
||||||
}
|
|
||||||
useSc = &sc
|
|
||||||
}
|
|
||||||
if useSc == nil {
|
|
||||||
return fmt.Errorf("no storage class found for %s", *storageClass)
|
|
||||||
}
|
|
||||||
|
|
||||||
flavor.Id = types.StringValue(*f.Id)
|
|
||||||
flavor.Description = types.StringValue(*f.Description)
|
|
||||||
foundFlavorCount++
|
|
||||||
}
|
|
||||||
for _, cls := range *f.StorageClasses {
|
|
||||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundFlavorCount > 1 {
|
|
||||||
return fmt.Errorf("multiple flavors found: %d flavors", foundFlavorCount)
|
|
||||||
}
|
|
||||||
if flavor.Id.ValueString() == "" {
|
|
||||||
return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFlavorModelById(ctx context.Context, client sqlserverflexClient, model *Model, flavor *flavorModel) error {
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
if flavor == nil {
|
|
||||||
return fmt.Errorf("nil flavor")
|
|
||||||
}
|
|
||||||
id := conversion.StringValueToPointer(flavor.Id)
|
|
||||||
if id == nil {
|
|
||||||
return fmt.Errorf("nil flavor ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
flavor.Id = types.StringValue("")
|
|
||||||
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
region := model.Region.ValueString()
|
|
||||||
|
|
||||||
flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
avl := ""
|
|
||||||
for _, f := range flavorList {
|
|
||||||
if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if *f.Id == *id {
|
|
||||||
flavor.Id = types.StringValue(*f.Id)
|
|
||||||
flavor.Description = types.StringValue(*f.Description)
|
|
||||||
flavor.CPU = types.Int64Value(*f.Cpu)
|
|
||||||
flavor.RAM = types.Int64Value(*f.Memory)
|
|
||||||
flavor.NodeType = types.StringValue(*f.NodeType)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
|
||||||
}
|
|
||||||
if flavor.Id.ValueString() == "" {
|
|
||||||
return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
|
||||||
"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/listplanmodifier"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||||
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||||
|
|
@ -57,7 +56,7 @@ type Model struct {
|
||||||
ProjectId types.String `tfsdk:"project_id"`
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
Name types.String `tfsdk:"name"`
|
Name types.String `tfsdk:"name"`
|
||||||
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
||||||
Flavor types.Object `tfsdk:"flavor"`
|
FlavorId types.String `tfsdk:"flavor_id"`
|
||||||
Encryption types.Object `tfsdk:"encryption"`
|
Encryption types.Object `tfsdk:"encryption"`
|
||||||
IsDeletable types.Bool `tfsdk:"is_deletable"`
|
IsDeletable types.Bool `tfsdk:"is_deletable"`
|
||||||
Storage types.Object `tfsdk:"storage"`
|
Storage types.Object `tfsdk:"storage"`
|
||||||
|
|
@ -98,24 +97,6 @@ var networkTypes = map[string]attr.Type{
|
||||||
"router_address": basetypes.StringType{},
|
"router_address": basetypes.StringType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct corresponding to Model.FlavorId
|
|
||||||
type flavorModel struct {
|
|
||||||
Id types.String `tfsdk:"id"`
|
|
||||||
Description types.String `tfsdk:"description"`
|
|
||||||
CPU types.Int64 `tfsdk:"cpu"`
|
|
||||||
RAM types.Int64 `tfsdk:"ram"`
|
|
||||||
NodeType types.String `tfsdk:"node_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types corresponding to flavorModel
|
|
||||||
var flavorTypes = map[string]attr.Type{
|
|
||||||
"id": basetypes.StringType{},
|
|
||||||
"description": basetypes.StringType{},
|
|
||||||
"cpu": basetypes.Int64Type{},
|
|
||||||
"ram": basetypes.Int64Type{},
|
|
||||||
"node_type": basetypes.StringType{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct corresponding to Model.Storage
|
// Struct corresponding to Model.Storage
|
||||||
type storageModel struct {
|
type storageModel struct {
|
||||||
Class types.String `tfsdk:"class"`
|
Class types.String `tfsdk:"class"`
|
||||||
|
|
@ -198,11 +179,13 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
"instance_id": "ID of the SQLServer Flex instance.",
|
"instance_id": "ID of the SQLServer Flex instance.",
|
||||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||||
"name": "Instance name.",
|
"name": "Instance name.",
|
||||||
"access_scope": "The access scope of the instance. (e.g. SNA)",
|
"access_scope": "The access scope of the instance. (SNA | PUBLIC)",
|
||||||
|
"flavor_id": "The flavor ID of the instance.",
|
||||||
"acl": "The Access Control List (ACL) for the SQLServer Flex instance.",
|
"acl": "The Access Control List (ACL) for the SQLServer Flex instance.",
|
||||||
"backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")`,
|
"backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")`,
|
||||||
"region": "The resource region. If not defined, the provider region is used.",
|
"region": "The resource region. If not defined, the provider region is used.",
|
||||||
"encryption": "The encryption block.",
|
"encryption": "The encryption block.",
|
||||||
|
"replicas": "The number of replicas of the SQLServer Flex instance.",
|
||||||
"network": "The network block.",
|
"network": "The network block.",
|
||||||
"keyring_id": "STACKIT KMS - KeyRing ID of the encryption key to use.",
|
"keyring_id": "STACKIT KMS - KeyRing ID of the encryption key to use.",
|
||||||
"key_id": "STACKIT KMS - Key ID of the encryption key to use.",
|
"key_id": "STACKIT KMS - Key ID of the encryption key to use.",
|
||||||
|
|
@ -271,78 +254,12 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
boolplanmodifier.UseStateForUnknown(),
|
boolplanmodifier.UseStateForUnknown(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO - make it either flavor_id or ram, cpu and node_type
|
"flavor_id": schema.StringAttribute{
|
||||||
"flavor": schema.SingleNestedAttribute{
|
PlanModifiers: []planmodifier.String{
|
||||||
PlanModifiers: []planmodifier.Object{
|
stringplanmodifier.RequiresReplace(),
|
||||||
objectplanmodifier.RequiresReplace(),
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
objectplanmodifier.UseStateForUnknown(),
|
|
||||||
},
|
},
|
||||||
Required: true,
|
Required: true,
|
||||||
Attributes: map[string]schema.Attribute{
|
|
||||||
"id": schema.StringAttribute{
|
|
||||||
Optional: true,
|
|
||||||
Computed: true,
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
stringplanmodifier.RequiresReplace(),
|
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"description": schema.StringAttribute{
|
|
||||||
Computed: true,
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"node_type": schema.StringAttribute{
|
|
||||||
Required: true,
|
|
||||||
PlanModifiers: []planmodifier.String{
|
|
||||||
stringplanmodifier.RequiresReplace(),
|
|
||||||
stringplanmodifier.UseStateForUnknown(),
|
|
||||||
},
|
|
||||||
Validators: []validator.String{
|
|
||||||
stringvalidator.ConflictsWith([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("id"),
|
|
||||||
}...),
|
|
||||||
stringvalidator.OneOfCaseInsensitive(validNodeTypes...),
|
|
||||||
stringvalidator.AlsoRequires([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("cpu"),
|
|
||||||
path.MatchRelative().AtParent().AtName("ram"),
|
|
||||||
}...),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"cpu": schema.Int64Attribute{
|
|
||||||
Required: true,
|
|
||||||
PlanModifiers: []planmodifier.Int64{
|
|
||||||
int64planmodifier.RequiresReplace(),
|
|
||||||
int64planmodifier.UseStateForUnknown(),
|
|
||||||
},
|
|
||||||
Validators: []validator.Int64{
|
|
||||||
int64validator.ConflictsWith([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("id"),
|
|
||||||
}...),
|
|
||||||
int64validator.AlsoRequires([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("node_type"),
|
|
||||||
path.MatchRelative().AtParent().AtName("ram"),
|
|
||||||
}...),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"ram": schema.Int64Attribute{
|
|
||||||
Required: true,
|
|
||||||
PlanModifiers: []planmodifier.Int64{
|
|
||||||
int64planmodifier.RequiresReplace(),
|
|
||||||
int64planmodifier.UseStateForUnknown(),
|
|
||||||
},
|
|
||||||
Validators: []validator.Int64{
|
|
||||||
int64validator.ConflictsWith([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("id"),
|
|
||||||
}...),
|
|
||||||
int64validator.AlsoRequires([]path.Expression{
|
|
||||||
path.MatchRelative().AtParent().AtName("node_type"),
|
|
||||||
path.MatchRelative().AtParent().AtName("cpu"),
|
|
||||||
}...),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"replicas": schema.Int64Attribute{
|
"replicas": schema.Int64Attribute{
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
|
@ -506,7 +423,6 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
|
|
||||||
// Create creates the resource and sets the initial Terraform state.
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from plan
|
|
||||||
var model Model
|
var model Model
|
||||||
diags := req.Plan.Get(ctx, &model)
|
diags := req.Plan.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(diags...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
@ -548,37 +464,6 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavor := &flavorModel{}
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flavor.Id.IsNull() || flavor.Id.IsUnknown() {
|
|
||||||
err := loadFlavorId(ctx, r.client, &model, flavor, storage)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
flavorValues := map[string]attr.Value{
|
|
||||||
"id": flavor.Id,
|
|
||||||
"description": flavor.Description,
|
|
||||||
"cpu": flavor.CPU,
|
|
||||||
"ram": flavor.RAM,
|
|
||||||
"node_type": flavor.NodeType,
|
|
||||||
}
|
|
||||||
var flavorObject basetypes.ObjectValue
|
|
||||||
flavorObject, diags = types.ObjectValue(flavorTypes, flavorValues)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if diags.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
model.Flavor = flavorObject
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate API request body from model
|
// Generate API request body from model
|
||||||
payload, err := toCreatePayload(&model, storage, encryption, network)
|
payload, err := toCreatePayload(&model, storage, encryption, network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -596,6 +481,7 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
|
||||||
|
|
||||||
instanceId := *createResp.Id
|
instanceId := *createResp.Id
|
||||||
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
|
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
|
||||||
|
"id": utils.BuildInternalTerraformId(projectId, region, instanceId),
|
||||||
"instance_id": instanceId,
|
"instance_id": instanceId,
|
||||||
})
|
})
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
|
|
@ -615,25 +501,8 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *waitResp.FlavorId != flavor.Id.ValueString() {
|
|
||||||
core.LogAndAddError(
|
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
"Error creating instance",
|
|
||||||
fmt.Sprintf("Instance creation waiting: returned flavor id differs (expected: %s, current: %s)", flavor.Id.ValueString(), *waitResp.FlavorId),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if flavor.CPU.IsNull() || flavor.CPU.IsUnknown() {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "Instance creation waiting: flavor cpu is null or unknown")
|
|
||||||
}
|
|
||||||
if flavor.RAM.IsNull() || flavor.RAM.IsUnknown() {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "Instance creation waiting: flavor ram is null or unknown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, waitResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -672,27 +541,6 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
var flavor = &flavorModel{}
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := getFlavorModelById(ctx, r.client, &model, flavor)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage = &storageModel{}
|
var storage = &storageModel{}
|
||||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||||
|
|
@ -734,7 +582,7 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
@ -795,37 +643,6 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavor := &flavorModel{}
|
|
||||||
if !model.Flavor.IsNull() && !model.Flavor.IsUnknown() {
|
|
||||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flavor.Id.IsNull() || flavor.Id.IsUnknown() {
|
|
||||||
err := loadFlavorId(ctx, r.client, &model, flavor, storage)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(err.Error(), err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
flavorValues := map[string]attr.Value{
|
|
||||||
"id": flavor.Id,
|
|
||||||
"description": flavor.Description,
|
|
||||||
"cpu": flavor.CPU,
|
|
||||||
"ram": flavor.RAM,
|
|
||||||
"node_type": flavor.NodeType,
|
|
||||||
}
|
|
||||||
var flavorObject basetypes.ObjectValue
|
|
||||||
flavorObject, diags = types.ObjectValue(flavorTypes, flavorValues)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if diags.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
model.Flavor = flavorObject
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate API request body from model
|
// Generate API request body from model
|
||||||
payload, err := toUpdatePayload(&model, storage, network)
|
payload, err := toUpdatePayload(&model, storage, network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -833,7 +650,8 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Update existing instance
|
// Update existing instance
|
||||||
err = r.client.UpdateInstancePartiallyRequest(ctx, projectId, region, instanceId).UpdateInstancePartiallyRequestPayload(*payload).Execute()
|
err = r.client.UpdateInstanceRequest(ctx, projectId, region, instanceId).UpdateInstanceRequestPayload(*payload).Execute()
|
||||||
|
// err = r.client.UpdateInstancePartiallyRequest(ctx, projectId, region, instanceId).UpdateInstancePartiallyRequestPayload(*payload).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
|
||||||
return
|
return
|
||||||
|
|
@ -848,7 +666,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map response body to schema
|
// Map response body to schema
|
||||||
err = mapFields(ctx, waitResp, &model, flavor, storage, encryption, network, region)
|
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ func TestMapFields(t *testing.T) {
|
||||||
description string
|
description string
|
||||||
state Model
|
state Model
|
||||||
input *sqlserverflex.GetInstanceResponse
|
input *sqlserverflex.GetInstanceResponse
|
||||||
flavor *flavorModel
|
|
||||||
storage *storageModel
|
storage *storageModel
|
||||||
encryption *encryptionModel
|
encryption *encryptionModel
|
||||||
network *networkModel
|
network *networkModel
|
||||||
|
|
@ -53,13 +52,6 @@ func TestMapFields(t *testing.T) {
|
||||||
Edition: types.StringValue("edition 1"),
|
Edition: types.StringValue("edition 1"),
|
||||||
Status: types.StringValue("status"),
|
Status: types.StringValue("status"),
|
||||||
IsDeletable: types.BoolValue(true),
|
IsDeletable: types.BoolValue(true),
|
||||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
|
||||||
"id": types.StringValue("flavor_id"),
|
|
||||||
"description": types.StringNull(),
|
|
||||||
"cpu": types.Int64Null(),
|
|
||||||
"ram": types.Int64Null(),
|
|
||||||
"node_type": types.StringNull(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
&sqlserverflex.GetInstanceResponse{
|
&sqlserverflex.GetInstanceResponse{
|
||||||
FlavorId: utils.Ptr("flavor_id"),
|
FlavorId: utils.Ptr("flavor_id"),
|
||||||
|
|
@ -70,9 +62,6 @@ func TestMapFields(t *testing.T) {
|
||||||
Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
|
Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
|
||||||
IsDeletable: utils.Ptr(true),
|
IsDeletable: utils.Ptr(true),
|
||||||
},
|
},
|
||||||
&flavorModel{
|
|
||||||
Id: types.StringValue("flavor_id"),
|
|
||||||
},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{
|
&networkModel{
|
||||||
|
|
@ -85,14 +74,7 @@ func TestMapFields(t *testing.T) {
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
Name: types.StringNull(),
|
Name: types.StringNull(),
|
||||||
BackupSchedule: types.StringNull(),
|
BackupSchedule: types.StringNull(),
|
||||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
Replicas: types.Int64Value(1),
|
||||||
"id": types.StringValue("flavor_id"),
|
|
||||||
"description": types.StringNull(),
|
|
||||||
"cpu": types.Int64Null(),
|
|
||||||
"ram": types.Int64Null(),
|
|
||||||
"node_type": types.StringNull(),
|
|
||||||
}),
|
|
||||||
Replicas: types.Int64Value(1),
|
|
||||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||||
"class": types.StringNull(),
|
"class": types.StringNull(),
|
||||||
"size": types.Int64Null(),
|
"size": types.Int64Null(),
|
||||||
|
|
@ -151,13 +133,6 @@ func TestMapFields(t *testing.T) {
|
||||||
RouterAddress: nil,
|
RouterAddress: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&flavorModel{
|
|
||||||
Id: basetypes.NewStringValue("flavor_id"),
|
|
||||||
Description: basetypes.NewStringValue("description"),
|
|
||||||
CPU: basetypes.NewInt64Value(12),
|
|
||||||
RAM: basetypes.NewInt64Value(34),
|
|
||||||
NodeType: basetypes.NewStringValue("node_type"),
|
|
||||||
},
|
|
||||||
&storageModel{},
|
&storageModel{},
|
||||||
&encryptionModel{},
|
&encryptionModel{},
|
||||||
&networkModel{
|
&networkModel{
|
||||||
|
|
@ -174,14 +149,7 @@ func TestMapFields(t *testing.T) {
|
||||||
ProjectId: types.StringValue("pid"),
|
ProjectId: types.StringValue("pid"),
|
||||||
Name: types.StringValue("name"),
|
Name: types.StringValue("name"),
|
||||||
BackupSchedule: types.StringValue("schedule"),
|
BackupSchedule: types.StringValue("schedule"),
|
||||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
Replicas: types.Int64Value(56),
|
||||||
"id": types.StringValue("flavor_id"),
|
|
||||||
"description": types.StringValue("description"),
|
|
||||||
"cpu": types.Int64Value(12),
|
|
||||||
"ram": types.Int64Value(34),
|
|
||||||
"node_type": types.StringValue("node_type"),
|
|
||||||
}),
|
|
||||||
Replicas: types.Int64Value(56),
|
|
||||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||||
"class": types.StringValue("class"),
|
"class": types.StringValue("class"),
|
||||||
"size": types.Int64Value(78),
|
"size": types.Int64Value(78),
|
||||||
|
|
@ -380,7 +348,7 @@ func TestMapFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
t.Run(tt.description, func(t *testing.T) {
|
||||||
err := mapFields(context.Background(), tt.input, &tt.state, tt.flavor, tt.storage, tt.encryption, tt.network, tt.region)
|
err := mapFields(context.Background(), tt.input, &tt.state, tt.storage, tt.encryption, tt.network, tt.region)
|
||||||
if !tt.isValid && err == nil {
|
if !tt.isValid && err == nil {
|
||||||
t.Fatalf("Should have failed")
|
t.Fatalf("Should have failed")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
|
||||||
core_config "github.com/stackitcloud/stackit-sdk-go/core/config"
|
coreconfig "github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/wait"
|
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/wait"
|
||||||
|
|
@ -28,6 +28,7 @@ var (
|
||||||
//go:embed testdata/resource-min.tf
|
//go:embed testdata/resource-min.tf
|
||||||
resourceMinConfig string
|
resourceMinConfig string
|
||||||
)
|
)
|
||||||
|
|
||||||
var testConfigVarsMin = config.Variables{
|
var testConfigVarsMin = config.Variables{
|
||||||
"project_id": config.StringVariable(testutil.ProjectId),
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
|
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
|
||||||
|
|
@ -440,7 +441,7 @@ func testAccChecksqlserverflexDestroy(s *terraform.State) error {
|
||||||
client, err = sqlserverflex.NewAPIClient()
|
client, err = sqlserverflex.NewAPIClient()
|
||||||
} else {
|
} else {
|
||||||
client, err = sqlserverflex.NewAPIClient(
|
client, err = sqlserverflex.NewAPIClient(
|
||||||
core_config.WithEndpoint(testutil.SQLServerFlexCustomEndpoint),
|
coreconfig.WithEndpoint(testutil.SQLServerFlexCustomEndpoint),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,15 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/features"
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/features"
|
||||||
sdkauth "github.com/stackitcloud/stackit-sdk-go/core/auth"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
|
||||||
|
|
||||||
postgresFlexAlphaDatabase "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database"
|
postgresFlexAlphaDatabase "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database"
|
||||||
|
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavor"
|
||||||
postgresFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance"
|
postgresFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance"
|
||||||
postgresFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user"
|
postgresFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user"
|
||||||
|
sqlserverFlexAlphaFlavor "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor"
|
||||||
sqlServerFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
|
sqlServerFlexAlphaInstance "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
|
||||||
sqlserverFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user"
|
sqlserverFlexAlphaUser "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user"
|
||||||
|
sdkauth "github.com/stackitcloud/stackit-sdk-go/core/auth"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure the implementation satisfies the expected interfaces
|
// Ensure the implementation satisfies the expected interfaces
|
||||||
|
|
@ -488,9 +489,13 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
|
||||||
// DataSources defines the data sources implemented in the provider.
|
// DataSources defines the data sources implemented in the provider.
|
||||||
func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource {
|
func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource {
|
||||||
return []func() datasource.DataSource{
|
return []func() datasource.DataSource{
|
||||||
|
postgresFlexAlphaFlavor.NewFlavorDataSource,
|
||||||
|
//postgresFlexAlphaFlavor.NewFlavorListDataSource,
|
||||||
postgresFlexAlphaDatabase.NewDatabaseDataSource,
|
postgresFlexAlphaDatabase.NewDatabaseDataSource,
|
||||||
postgresFlexAlphaInstance.NewInstanceDataSource,
|
postgresFlexAlphaInstance.NewInstanceDataSource,
|
||||||
postgresFlexAlphaUser.NewUserDataSource,
|
postgresFlexAlphaUser.NewUserDataSource,
|
||||||
|
|
||||||
|
sqlserverFlexAlphaFlavor.NewFlavorDataSource,
|
||||||
sqlServerFlexAlphaInstance.NewInstanceDataSource,
|
sqlServerFlexAlphaInstance.NewInstanceDataSource,
|
||||||
sqlserverFlexAlphaUser.NewUserDataSource,
|
sqlserverFlexAlphaUser.NewUserDataSource,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue