Remove CRUD method logic from PostgreSQL (first step of removal) (#423)
* Remove CRUD method logic from PostgreSQL (first step of removal) * remove comment * removed unused vars and parameters * move verb tense to the past * also datasource * apply change to credential * improve error message, remove acc testing * update docs
This commit is contained in:
parent
2a0998f511
commit
e553628b5e
11 changed files with 40 additions and 1628 deletions
|
|
@ -4,14 +4,14 @@ page_title: "stackit_postgresql_credential Data Source - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
PostgreSQL credential data source schema. Must have a region specified in the provider configuration.
|
PostgreSQL credential data source schema. Must have a region specified in the provider configuration.
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Data sources of this type will stop working after that. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
---
|
---
|
||||||
|
|
||||||
# stackit_postgresql_credential (Data Source)
|
# stackit_postgresql_credential (Data Source)
|
||||||
|
|
||||||
PostgreSQL credential data source schema. Must have a `region` specified in the provider configuration.
|
PostgreSQL credential data source schema. Must have a `region` specified in the provider configuration.
|
||||||
|
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Data sources of this type will stop working after that. Use stackit_postgresflex_user instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_user instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ page_title: "stackit_postgresql_instance Data Source - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
PostgreSQL instance data source schema. Must have a region specified in the provider configuration.
|
PostgreSQL instance data source schema. Must have a region specified in the provider configuration.
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Data sources of this type will stop working after that. Use stackitpostgresflexinstance instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexinstance instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
---
|
---
|
||||||
|
|
||||||
# stackit_postgresql_instance (Data Source)
|
# stackit_postgresql_instance (Data Source)
|
||||||
|
|
||||||
PostgreSQL instance data source schema. Must have a `region` specified in the provider configuration.
|
PostgreSQL instance data source schema. Must have a `region` specified in the provider configuration.
|
||||||
|
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Data sources of this type will stop working after that. Use stackit_postgresflex_instance instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ page_title: "stackit_postgresql_credential Resource - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
PostgreSQL credential resource schema. Must have a region specified in the provider configuration.
|
PostgreSQL credential resource schema. Must have a region specified in the provider configuration.
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Resources of this type will stop working after that. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexuser instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
---
|
---
|
||||||
|
|
||||||
# stackit_postgresql_credential (Resource)
|
# stackit_postgresql_credential (Resource)
|
||||||
|
|
||||||
PostgreSQL credential resource schema. Must have a `region` specified in the provider configuration.
|
PostgreSQL credential resource schema. Must have a `region` specified in the provider configuration.
|
||||||
|
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Resources of this type will stop working after that. Use stackit_postgresflex_user instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_user instead. For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ page_title: "stackit_postgresql_instance Resource - stackit"
|
||||||
subcategory: ""
|
subcategory: ""
|
||||||
description: |-
|
description: |-
|
||||||
PostgreSQL instance resource schema. Must have a region specified in the provider configuration.
|
PostgreSQL instance resource schema. Must have a region specified in the provider configuration.
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Resources of this type will stop working after that. Use stackitpostgresflexinstance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block (https://developer.hashicorp.com/terraform/language/import)
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackitpostgresflexinstance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block (https://developer.hashicorp.com/terraform/language/import)
|
||||||
---
|
---
|
||||||
|
|
||||||
# stackit_postgresql_instance (Resource)
|
# stackit_postgresql_instance (Resource)
|
||||||
|
|
||||||
PostgreSQL instance resource schema. Must have a `region` specified in the provider configuration.
|
PostgreSQL instance resource schema. Must have a `region` specified in the provider configuration.
|
||||||
|
|
||||||
!> The STACKIT PostgreSQL service will reach its end of support on June 30th 2024. Resources of this type will stop working after that. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block (https://developer.hashicorp.com/terraform/language/import)
|
!> The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an "import" block (https://developer.hashicorp.com/terraform/language/import)
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package postgresql
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
|
@ -15,7 +14,6 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -81,8 +79,8 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
|
||||||
"main": "PostgreSQL credential data source schema. Must have a `region` specified in the provider configuration.",
|
"main": "PostgreSQL credential data source schema. Must have a `region` specified in the provider configuration.",
|
||||||
"deprecation_message": strings.Join(
|
"deprecation_message": strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
"The STACKIT PostgreSQL service will reach its end of support on June 30th 2024.",
|
"The STACKIT PostgreSQL service has reached its end of support on June 30th 2024.",
|
||||||
"Data sources of this type will stop working after that.",
|
"Resources of this type have stopped working since then.",
|
||||||
"Use stackit_postgresflex_user instead.",
|
"Use stackit_postgresflex_user instead.",
|
||||||
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
||||||
},
|
},
|
||||||
|
|
@ -160,42 +158,6 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read refreshes the Terraform state with the latest data.
|
// Read refreshes the Terraform state with the latest data.
|
||||||
func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *credentialDataSource) Read(ctx context.Context, _ datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential data", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.Config.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
credentialId := model.CredentialId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
ctx = tflog.SetField(ctx, "credential_id", credentialId)
|
|
||||||
|
|
||||||
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
|
||||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
|
||||||
resp.State.RemoveResource(ctx)
|
|
||||||
}
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(recordSetResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set refreshed state
|
|
||||||
diags = resp.State.Set(ctx, model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL credential read")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package postgresql
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
|
@ -11,17 +10,13 @@ import (
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||||
"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/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql/wait"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure the implementation satisfies the expected interfaces.
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
|
@ -103,8 +98,8 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
|
||||||
"main": "PostgreSQL credential resource schema. Must have a `region` specified in the provider configuration.",
|
"main": "PostgreSQL credential resource schema. Must have a `region` specified in the provider configuration.",
|
||||||
"deprecation_message": strings.Join(
|
"deprecation_message": strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
"The STACKIT PostgreSQL service will reach its end of support on June 30th 2024.",
|
"The STACKIT PostgreSQL service has reached its end of support on June 30th 2024.",
|
||||||
"Resources of this type will stop working after that.",
|
"Resources of this type have stopped working since then.",
|
||||||
"Use stackit_postgresflex_user instead.",
|
"Use stackit_postgresflex_user instead.",
|
||||||
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
||||||
},
|
},
|
||||||
|
|
@ -196,196 +191,27 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates the resource and sets the initial Terraform state.
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *credentialResource) Create(ctx context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.Plan.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
// Create new recordset
|
|
||||||
credentialsResp, err := r.client.CreateCredentials(ctx, projectId, instanceId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if credentialsResp.Id == nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", "Got empty credential id")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
credentialId := *credentialsResp.Id
|
|
||||||
ctx = tflog.SetField(ctx, "credential_id", credentialId)
|
|
||||||
|
|
||||||
waitResp, err := wait.CreateCredentialsWaitHandler(ctx, r.client, projectId, instanceId, credentialId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Instance creation waiting: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(waitResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
diags = resp.State.Set(ctx, model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL credential created")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read refreshes the Terraform state with the latest data.
|
// Read refreshes the Terraform state with the latest data.
|
||||||
func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *credentialResource) Read(ctx context.Context, _ resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.State.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
credentialId := model.CredentialId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
ctx = tflog.SetField(ctx, "credential_id", credentialId)
|
|
||||||
|
|
||||||
recordSetResp, err := r.client.GetCredentials(ctx, projectId, instanceId, credentialId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
|
||||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
|
||||||
resp.State.RemoveResource(ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(recordSetResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credential", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set refreshed state
|
|
||||||
diags = resp.State.Set(ctx, model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL credential 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 *credentialResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *credentialResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Update shouldn't be called
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating credential", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating credential", "Credential can't be updated")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the resource and removes the Terraform state on success.
|
// Delete deletes the resource and removes the Terraform state on success.
|
||||||
func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *credentialResource) Delete(ctx context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.State.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
credentialId := model.CredentialId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
ctx = tflog.SetField(ctx, "credential_id", credentialId)
|
|
||||||
|
|
||||||
// Delete existing record set
|
|
||||||
err := r.client.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
}
|
|
||||||
_, err = wait.DeleteCredentialsWaitHandler(ctx, r.client, projectId, instanceId, credentialId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Instance deletion waiting: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL credential deleted")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportState imports a resource into the Terraform state on success.
|
// ImportState imports a resource into the Terraform state on success.
|
||||||
// The expected format of the resource import identifier is: project_id,instance_id,credential_id
|
// The expected format of the resource import identifier is: project_id,instance_id
|
||||||
func (r *credentialResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
func (r *credentialResource) ImportState(ctx context.Context, _ resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||||
idParts := strings.Split(req.ID, core.Separator)
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error importing credential", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
|
||||||
"Error importing credential",
|
|
||||||
fmt.Sprintf("Expected import identifier with format [project_id],[instance_id],[credential_id], got %q", req.ID),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
|
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credential_id"), idParts[2])...)
|
|
||||||
tflog.Info(ctx, "PostgreSQL credential state imported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapFields(credentialsResp *postgresql.CredentialsResponse, model *Model) error {
|
|
||||||
if credentialsResp == nil {
|
|
||||||
return fmt.Errorf("response input is nil")
|
|
||||||
}
|
|
||||||
if credentialsResp.Raw == nil {
|
|
||||||
return fmt.Errorf("response credentials raw is nil")
|
|
||||||
}
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("model input is nil")
|
|
||||||
}
|
|
||||||
credentials := credentialsResp.Raw.Credentials
|
|
||||||
|
|
||||||
var credentialId string
|
|
||||||
if model.CredentialId.ValueString() != "" {
|
|
||||||
credentialId = model.CredentialId.ValueString()
|
|
||||||
} else if credentialsResp.Id != nil {
|
|
||||||
credentialId = *credentialsResp.Id
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("credentials id not present")
|
|
||||||
}
|
|
||||||
|
|
||||||
idParts := []string{
|
|
||||||
model.ProjectId.ValueString(),
|
|
||||||
model.InstanceId.ValueString(),
|
|
||||||
credentialId,
|
|
||||||
}
|
|
||||||
model.Id = types.StringValue(
|
|
||||||
strings.Join(idParts, core.Separator),
|
|
||||||
)
|
|
||||||
model.CredentialId = types.StringValue(credentialId)
|
|
||||||
model.Hosts = types.ListNull(types.StringType)
|
|
||||||
if credentials != nil {
|
|
||||||
if credentials.Hosts != nil {
|
|
||||||
var hosts []attr.Value
|
|
||||||
for _, host := range *credentials.Hosts {
|
|
||||||
hosts = append(hosts, types.StringValue(host))
|
|
||||||
}
|
|
||||||
hostsList, diags := types.ListValue(types.StringType, hosts)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("failed to map hosts: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
model.Hosts = hostsList
|
|
||||||
}
|
|
||||||
model.Host = types.StringPointerValue(credentials.Host)
|
|
||||||
model.HttpAPIURI = types.StringPointerValue(credentials.HttpApiUri)
|
|
||||||
model.Name = types.StringPointerValue(credentials.Name)
|
|
||||||
model.Password = types.StringPointerValue(credentials.Password)
|
|
||||||
model.Port = types.Int64PointerValue(credentials.Port)
|
|
||||||
model.Uri = types.StringPointerValue(credentials.Uri)
|
|
||||||
model.Username = types.StringPointerValue(credentials.Username)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
package postgresql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMapFields(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *postgresql.CredentialsResponse
|
|
||||||
expected Model
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&postgresql.CredentialsResponse{
|
|
||||||
Id: utils.Ptr("cid"),
|
|
||||||
Raw: &postgresql.RawCredentials{},
|
|
||||||
},
|
|
||||||
Model{
|
|
||||||
Id: types.StringValue("pid,iid,cid"),
|
|
||||||
CredentialId: types.StringValue("cid"),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Host: types.StringNull(),
|
|
||||||
Hosts: types.ListNull(types.StringType),
|
|
||||||
HttpAPIURI: types.StringNull(),
|
|
||||||
Name: types.StringNull(),
|
|
||||||
Password: types.StringNull(),
|
|
||||||
Port: types.Int64Null(),
|
|
||||||
Uri: types.StringNull(),
|
|
||||||
Username: types.StringNull(),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"simple_values",
|
|
||||||
&postgresql.CredentialsResponse{
|
|
||||||
Id: utils.Ptr("cid"),
|
|
||||||
Raw: &postgresql.RawCredentials{
|
|
||||||
Credentials: &postgresql.Credentials{
|
|
||||||
Host: utils.Ptr("host"),
|
|
||||||
Hosts: &[]string{
|
|
||||||
"host_1",
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
HttpApiUri: utils.Ptr("http"),
|
|
||||||
Name: utils.Ptr("name"),
|
|
||||||
Password: utils.Ptr("password"),
|
|
||||||
Port: utils.Ptr(int64(1234)),
|
|
||||||
Uri: utils.Ptr("uri"),
|
|
||||||
Username: utils.Ptr("username"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{
|
|
||||||
Id: types.StringValue("pid,iid,cid"),
|
|
||||||
CredentialId: types.StringValue("cid"),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Host: types.StringValue("host"),
|
|
||||||
Hosts: types.ListValueMust(types.StringType, []attr.Value{
|
|
||||||
types.StringValue("host_1"),
|
|
||||||
types.StringValue(""),
|
|
||||||
}),
|
|
||||||
HttpAPIURI: types.StringValue("http"),
|
|
||||||
Name: types.StringValue("name"),
|
|
||||||
Password: types.StringValue("password"),
|
|
||||||
Port: types.Int64Value(1234),
|
|
||||||
Uri: types.StringValue("uri"),
|
|
||||||
Username: types.StringValue("username"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"null_fields_and_int_conversions",
|
|
||||||
&postgresql.CredentialsResponse{
|
|
||||||
Id: utils.Ptr("cid"),
|
|
||||||
Raw: &postgresql.RawCredentials{
|
|
||||||
Credentials: &postgresql.Credentials{
|
|
||||||
Host: utils.Ptr(""),
|
|
||||||
Hosts: &[]string{},
|
|
||||||
HttpApiUri: nil,
|
|
||||||
Name: nil,
|
|
||||||
Password: nil,
|
|
||||||
Port: utils.Ptr(int64(2123456789)),
|
|
||||||
Uri: nil,
|
|
||||||
Username: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{
|
|
||||||
Id: types.StringValue("pid,iid,cid"),
|
|
||||||
CredentialId: types.StringValue("cid"),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Host: types.StringValue(""),
|
|
||||||
Hosts: types.ListValueMust(types.StringType, []attr.Value{}),
|
|
||||||
HttpAPIURI: types.StringNull(),
|
|
||||||
Name: types.StringNull(),
|
|
||||||
Password: types.StringNull(),
|
|
||||||
Port: types.Int64Value(2123456789),
|
|
||||||
Uri: types.StringNull(),
|
|
||||||
Username: types.StringNull(),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response",
|
|
||||||
nil,
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_resource_id",
|
|
||||||
&postgresql.CredentialsResponse{},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_raw_credential",
|
|
||||||
&postgresql.CredentialsResponse{
|
|
||||||
Id: utils.Ptr("cid"),
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
|
||||||
state := &Model{
|
|
||||||
ProjectId: tt.expected.ProjectId,
|
|
||||||
InstanceId: tt.expected.InstanceId,
|
|
||||||
}
|
|
||||||
err := mapFields(tt.input, state)
|
|
||||||
if !tt.isValid && err == nil {
|
|
||||||
t.Fatalf("Should have failed")
|
|
||||||
}
|
|
||||||
if tt.isValid && err != nil {
|
|
||||||
t.Fatalf("Should not have failed: %v", err)
|
|
||||||
}
|
|
||||||
if tt.isValid {
|
|
||||||
diff := cmp.Diff(state, &tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,6 @@ package postgresql
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
|
@ -15,7 +14,6 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -81,8 +79,8 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
|
||||||
"main": "PostgreSQL instance data source schema. Must have a `region` specified in the provider configuration.",
|
"main": "PostgreSQL instance data source schema. Must have a `region` specified in the provider configuration.",
|
||||||
"deprecation_message": strings.Join(
|
"deprecation_message": strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
"The STACKIT PostgreSQL service will reach its end of support on June 30th 2024.",
|
"The STACKIT PostgreSQL service has reached its end of support on June 30th 2024.",
|
||||||
"Data sources of this type will stop working after that.",
|
"Resources of this type have stopped working since then.",
|
||||||
"Use stackit_postgresflex_instance instead.",
|
"Use stackit_postgresflex_instance instead.",
|
||||||
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
"For more details, check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html",
|
||||||
},
|
},
|
||||||
|
|
@ -183,46 +181,6 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read refreshes the Terraform state with the latest data.
|
// Read refreshes the Terraform state with the latest data.
|
||||||
func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *instanceDataSource) Read(ctx context.Context, _ datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance data", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.Config.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
|
||||||
if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
|
|
||||||
resp.State.RemoveResource(ctx)
|
|
||||||
}
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = mapFields(instanceResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute and store values not present in the API response
|
|
||||||
err = loadPlanNameAndVersion(ctx, r.client, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Loading service plan details: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set refreshed state
|
|
||||||
diags = resp.State.Set(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL instance read")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,21 @@ package postgresql
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"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/schema/validator"
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||||
|
|
||||||
"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/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/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql/wait"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure the implementation satisfies the expected interfaces.
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
|
@ -50,26 +43,6 @@ type Model struct {
|
||||||
PlanId types.String `tfsdk:"plan_id"`
|
PlanId types.String `tfsdk:"plan_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct corresponding to DataSourceModel.Parameters
|
|
||||||
type parametersModel struct {
|
|
||||||
EnableMonitoring types.Bool `tfsdk:"enable_monitoring"`
|
|
||||||
MetricsFrequency types.Int64 `tfsdk:"metrics_frequency"`
|
|
||||||
MetricsPrefix types.String `tfsdk:"metrics_prefix"`
|
|
||||||
MonitoringInstanceId types.String `tfsdk:"monitoring_instance_id"`
|
|
||||||
Plugins types.List `tfsdk:"plugins"`
|
|
||||||
SgwAcl types.String `tfsdk:"sgw_acl"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types corresponding to parametersModel
|
|
||||||
var parametersTypes = map[string]attr.Type{
|
|
||||||
"enable_monitoring": basetypes.BoolType{},
|
|
||||||
"metrics_frequency": basetypes.Int64Type{},
|
|
||||||
"metrics_prefix": basetypes.StringType{},
|
|
||||||
"monitoring_instance_id": basetypes.StringType{},
|
|
||||||
"plugins": basetypes.ListType{ElemType: types.StringType},
|
|
||||||
"sgw_acl": basetypes.StringType{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInstanceResource is a helper function to simplify the provider implementation.
|
// NewInstanceResource is a helper function to simplify the provider implementation.
|
||||||
func NewInstanceResource() resource.Resource {
|
func NewInstanceResource() resource.Resource {
|
||||||
return &instanceResource{}
|
return &instanceResource{}
|
||||||
|
|
@ -127,8 +100,8 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
||||||
"main": "PostgreSQL instance resource schema. Must have a `region` specified in the provider configuration.",
|
"main": "PostgreSQL instance resource schema. Must have a `region` specified in the provider configuration.",
|
||||||
"deprecation_message": strings.Join(
|
"deprecation_message": strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
"The STACKIT PostgreSQL service will reach its end of support on June 30th 2024.",
|
"The STACKIT PostgreSQL service has reached its end of support on June 30th 2024.",
|
||||||
"Resources of this type will stop working after that.",
|
"Resources of this type have stopped working since then.",
|
||||||
"Use stackit_postgresflex_instance instead.",
|
"Use stackit_postgresflex_instance instead.",
|
||||||
"Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)",
|
"Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)",
|
||||||
},
|
},
|
||||||
|
|
@ -263,488 +236,27 @@ 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, _ resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.Plan.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
|
|
||||||
var parameters = ¶metersModel{}
|
|
||||||
var parametersPlugins *[]string
|
|
||||||
if !(model.Parameters.IsNull() || model.Parameters.IsUnknown()) {
|
|
||||||
diags = model.Parameters.As(ctx, parameters, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !(parameters.Plugins.IsNull() || parameters.Plugins.IsUnknown()) {
|
|
||||||
var pp []types.String
|
|
||||||
var res []string
|
|
||||||
diags = parameters.Plugins.ElementsAs(ctx, &pp, false)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
for _, v := range pp {
|
|
||||||
res = append(res, v.ValueString())
|
|
||||||
}
|
|
||||||
parametersPlugins = &res
|
|
||||||
}
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.loadPlanId(ctx, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading service plan: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate API request body from model
|
|
||||||
payload, err := toCreatePayload(&model, parameters, parametersPlugins)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Create new instance
|
|
||||||
createResp, err := r.client.CreateInstance(ctx, projectId).CreateInstancePayload(*payload).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
instanceId := *createResp.InstanceId
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Instance creation waiting: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(waitResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", 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, "Postgresql instance created")
|
|
||||||
}
|
|
||||||
|
|
||||||
func toCreatePayload(model *Model, parameters *parametersModel, parametersPlugins *[]string) (*postgresql.CreateInstancePayload, error) {
|
|
||||||
if model == nil {
|
|
||||||
return nil, fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
|
|
||||||
if parameters == nil {
|
|
||||||
return &postgresql.CreateInstancePayload{
|
|
||||||
InstanceName: conversion.StringValueToPointer(model.Name),
|
|
||||||
PlanId: conversion.StringValueToPointer(model.PlanId),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &postgresql.CreateInstancePayload{
|
|
||||||
InstanceName: conversion.StringValueToPointer(model.Name),
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: conversion.BoolValueToPointer(parameters.EnableMonitoring),
|
|
||||||
MetricsFrequency: conversion.Int64ValueToPointer(parameters.MetricsFrequency),
|
|
||||||
MetricsPrefix: conversion.StringValueToPointer(parameters.MetricsPrefix),
|
|
||||||
MonitoringInstanceId: conversion.StringValueToPointer(parameters.MonitoringInstanceId),
|
|
||||||
Plugins: parametersPlugins,
|
|
||||||
SgwAcl: conversion.StringValueToPointer(parameters.SgwAcl),
|
|
||||||
},
|
|
||||||
PlanId: conversion.StringValueToPointer(model.PlanId),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read refreshes the Terraform state with the latest data.
|
// Read refreshes the Terraform state with the latest data.
|
||||||
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
func (r *instanceResource) Read(ctx context.Context, _ resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.State.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
|
||||||
if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) {
|
|
||||||
resp.State.RemoveResource(ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(instanceResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute and store values not present in the API response
|
|
||||||
err = loadPlanNameAndVersion(ctx, r.client, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Loading service plan details: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set refreshed state
|
|
||||||
diags = resp.State.Set(ctx, model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL 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, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
var model Model
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
diags := req.Plan.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
var parameters = ¶metersModel{}
|
|
||||||
var parametersPlugins *[]string
|
|
||||||
if !(model.Parameters.IsNull() || model.Parameters.IsUnknown()) {
|
|
||||||
diags = model.Parameters.As(ctx, parameters, basetypes.ObjectAsOptions{})
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !(parameters.Plugins.IsNull() || parameters.Plugins.IsUnknown()) {
|
|
||||||
var pp []types.String
|
|
||||||
var res []string
|
|
||||||
diags = parameters.Plugins.ElementsAs(ctx, &pp, false)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
for _, v := range pp {
|
|
||||||
res = append(res, v.ValueString())
|
|
||||||
}
|
|
||||||
parametersPlugins = &res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.loadPlanId(ctx, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading service plan: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate API request body from model
|
|
||||||
payload, err := toUpdatePayload(&model, parameters, parametersPlugins)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Update existing instance
|
|
||||||
err = r.client.PartialUpdateInstance(ctx, projectId, instanceId).PartialUpdateInstancePayload(*payload).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
waitResp, err := wait.PartialUpdateInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapFields(waitResp, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
diags = resp.State.Set(ctx, model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL instance updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
func toUpdatePayload(model *Model, parameters *parametersModel, parametersPlugins *[]string) (*postgresql.PartialUpdateInstancePayload, error) {
|
|
||||||
if model == nil {
|
|
||||||
return nil, fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
|
|
||||||
if parameters == nil {
|
|
||||||
return &postgresql.PartialUpdateInstancePayload{
|
|
||||||
PlanId: conversion.StringValueToPointer(model.PlanId),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return &postgresql.PartialUpdateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: conversion.BoolValueToPointer(parameters.EnableMonitoring),
|
|
||||||
MetricsFrequency: conversion.Int64ValueToPointer(parameters.MetricsFrequency),
|
|
||||||
MetricsPrefix: conversion.StringValueToPointer(parameters.MetricsPrefix),
|
|
||||||
MonitoringInstanceId: conversion.StringValueToPointer(parameters.MonitoringInstanceId),
|
|
||||||
Plugins: parametersPlugins,
|
|
||||||
SgwAcl: conversion.StringValueToPointer(parameters.SgwAcl),
|
|
||||||
},
|
|
||||||
PlanId: conversion.StringValueToPointer(model.PlanId),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, _ resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
// Retrieve values from state
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
var model Model
|
|
||||||
diags := req.State.Get(ctx, &model)
|
|
||||||
resp.Diagnostics.Append(diags...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
instanceId := model.InstanceId.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
// Delete existing instance
|
|
||||||
err := r.client.DeleteInstance(ctx, projectId, instanceId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tflog.Info(ctx, "PostgreSQL instance deleted")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportState imports a resource into the Terraform state on success.
|
// ImportState imports a resource into the Terraform state on success.
|
||||||
// The expected format of the resource import identifier is: project_id,instance_id
|
// The expected format of the resource import identifier is: project_id,instance_id
|
||||||
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
func (r *instanceResource) ImportState(ctx context.Context, _ resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||||
idParts := strings.Split(req.ID, core.Separator)
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error importing instance", "The STACKIT PostgreSQL service has reached its end of support on June 30th 2024. Resources of this type have stopped working since then. Use stackit_postgresflex_instance instead. Check https://docs.stackit.cloud/stackit/en/bring-your-data-to-stackit-postgresql-flex-138347648.html on how to backup and restore an instance from PostgreSQL to PostgreSQL Flex, then import the resource to Terraform using an \"import\" block (https://developer.hashicorp.com/terraform/language/import)")
|
||||||
|
|
||||||
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
|
||||||
"Error importing instance",
|
|
||||||
fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id] Got: %q", req.ID),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
|
|
||||||
tflog.Info(ctx, "PostgreSQL instance state imported")
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapFields(instance *postgresql.Instance, model *Model) error {
|
|
||||||
if instance == nil {
|
|
||||||
return fmt.Errorf("response input is nil")
|
|
||||||
}
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("model input is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
var instanceId string
|
|
||||||
if model.InstanceId.ValueString() != "" {
|
|
||||||
instanceId = model.InstanceId.ValueString()
|
|
||||||
} else if instance.InstanceId != nil {
|
|
||||||
instanceId = *instance.InstanceId
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("instance id not present")
|
|
||||||
}
|
|
||||||
|
|
||||||
idParts := []string{
|
|
||||||
model.ProjectId.ValueString(),
|
|
||||||
instanceId,
|
|
||||||
}
|
|
||||||
model.Id = types.StringValue(
|
|
||||||
strings.Join(idParts, core.Separator),
|
|
||||||
)
|
|
||||||
model.InstanceId = types.StringValue(instanceId)
|
|
||||||
model.PlanId = types.StringPointerValue(instance.PlanId)
|
|
||||||
model.CfGuid = types.StringPointerValue(instance.CfGuid)
|
|
||||||
model.CfSpaceGuid = types.StringPointerValue(instance.CfSpaceGuid)
|
|
||||||
model.DashboardUrl = types.StringPointerValue(instance.DashboardUrl)
|
|
||||||
model.ImageUrl = types.StringPointerValue(instance.ImageUrl)
|
|
||||||
model.Name = types.StringPointerValue(instance.Name)
|
|
||||||
model.CfOrganizationGuid = types.StringPointerValue(instance.CfOrganizationGuid)
|
|
||||||
|
|
||||||
if instance.Parameters == nil {
|
|
||||||
model.Parameters = types.ObjectNull(parametersTypes)
|
|
||||||
} else {
|
|
||||||
parameters, err := mapParameters(*instance.Parameters)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("mapping parameters: %w", err)
|
|
||||||
}
|
|
||||||
model.Parameters = parameters
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapParameters(params map[string]interface{}) (types.Object, error) {
|
|
||||||
attributes := map[string]attr.Value{}
|
|
||||||
for attribute := range parametersTypes {
|
|
||||||
valueInterface, ok := params[attribute]
|
|
||||||
if !ok {
|
|
||||||
// All fields are optional, so this is ok
|
|
||||||
// Set the value as nil, will be handled accordingly
|
|
||||||
valueInterface = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var value attr.Value
|
|
||||||
switch parametersTypes[attribute].(type) {
|
|
||||||
default:
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found unexpected attribute type '%T'", parametersTypes[attribute])
|
|
||||||
case basetypes.StringType:
|
|
||||||
if valueInterface == nil {
|
|
||||||
value = types.StringNull()
|
|
||||||
} else {
|
|
||||||
valueString, ok := valueInterface.(string)
|
|
||||||
if !ok {
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found attribute '%s' of type %T, failed to assert as string", attribute, valueInterface)
|
|
||||||
}
|
|
||||||
value = types.StringValue(valueString)
|
|
||||||
}
|
|
||||||
case basetypes.BoolType:
|
|
||||||
if valueInterface == nil {
|
|
||||||
value = types.BoolNull()
|
|
||||||
} else {
|
|
||||||
valueBool, ok := valueInterface.(bool)
|
|
||||||
if !ok {
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found attribute '%s' of type %T, failed to assert as bool", attribute, valueInterface)
|
|
||||||
}
|
|
||||||
value = types.BoolValue(valueBool)
|
|
||||||
}
|
|
||||||
case basetypes.Int64Type:
|
|
||||||
if valueInterface == nil {
|
|
||||||
value = types.Int64Null()
|
|
||||||
} else {
|
|
||||||
// This may be int64, int32, int or float64
|
|
||||||
// We try to assert all 4
|
|
||||||
var valueInt64 int64
|
|
||||||
switch temp := valueInterface.(type) {
|
|
||||||
default:
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found attribute '%s' of type %T, failed to assert as int", attribute, valueInterface)
|
|
||||||
case int64:
|
|
||||||
valueInt64 = temp
|
|
||||||
case int32:
|
|
||||||
valueInt64 = int64(temp)
|
|
||||||
case int:
|
|
||||||
valueInt64 = int64(temp)
|
|
||||||
case float64:
|
|
||||||
valueInt64 = int64(temp)
|
|
||||||
}
|
|
||||||
value = types.Int64Value(valueInt64)
|
|
||||||
}
|
|
||||||
case basetypes.ListType: // Assumed to be a list of strings
|
|
||||||
if valueInterface == nil {
|
|
||||||
value = types.ListNull(types.StringType)
|
|
||||||
} else {
|
|
||||||
// This may be []string{} or []interface{}
|
|
||||||
// We try to assert all 2
|
|
||||||
var valueList []attr.Value
|
|
||||||
switch temp := valueInterface.(type) {
|
|
||||||
default:
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found attribute '%s' of type %T, failed to assert as array of interface", attribute, valueInterface)
|
|
||||||
case []string:
|
|
||||||
for _, x := range temp {
|
|
||||||
valueList = append(valueList, types.StringValue(x))
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
for _, x := range temp {
|
|
||||||
xString, ok := x.(string)
|
|
||||||
if !ok {
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("found attribute '%s' with element '%s' of type %T, failed to assert as string", attribute, x, x)
|
|
||||||
}
|
|
||||||
valueList = append(valueList, types.StringValue(xString))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
temp2, diags := types.ListValue(types.StringType, valueList)
|
|
||||||
if diags.HasError() {
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("failed to map %s: %w", attribute, core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
value = temp2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributes[attribute] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
output, diags := types.ObjectValue(parametersTypes, attributes)
|
|
||||||
if diags.HasError() {
|
|
||||||
return types.ObjectNull(parametersTypes), fmt.Errorf("failed to create object: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *instanceResource) loadPlanId(ctx context.Context, model *Model) error {
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
res, err := r.client.ListOfferings(ctx, projectId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting PostgreSQL offerings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
version := model.Version.ValueString()
|
|
||||||
planName := model.PlanName.ValueString()
|
|
||||||
availableVersions := ""
|
|
||||||
availablePlanNames := ""
|
|
||||||
isValidVersion := false
|
|
||||||
for _, offer := range *res.Offerings {
|
|
||||||
if !strings.EqualFold(*offer.Version, version) {
|
|
||||||
availableVersions = fmt.Sprintf("%s\n- %s", availableVersions, *offer.Version)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
isValidVersion = true
|
|
||||||
|
|
||||||
for _, plan := range *offer.Plans {
|
|
||||||
if plan.Name == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.EqualFold(*plan.Name, planName) && plan.Id != nil {
|
|
||||||
model.PlanId = types.StringPointerValue(plan.Id)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
availablePlanNames = fmt.Sprintf("%s\n- %s", availablePlanNames, *plan.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidVersion {
|
|
||||||
return fmt.Errorf("couldn't find version '%s', available versions are: %s", version, availableVersions)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("couldn't find plan_name '%s' for version %s, available names are: %s", planName, version, availablePlanNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPlanNameAndVersion(ctx context.Context, client *postgresql.APIClient, model *Model) error {
|
|
||||||
projectId := model.ProjectId.ValueString()
|
|
||||||
planId := model.PlanId.ValueString()
|
|
||||||
res, err := client.ListOfferings(ctx, projectId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting PostgreSQL offerings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, offer := range *res.Offerings {
|
|
||||||
for _, plan := range *offer.Plans {
|
|
||||||
if strings.EqualFold(*plan.Id, planId) && plan.Id != nil {
|
|
||||||
model.PlanName = types.StringPointerValue(plan.Name)
|
|
||||||
model.Version = types.StringPointerValue(offer.Version)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("couldn't find plan_name and version for plan_id '%s'", planId)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,435 +0,0 @@
|
||||||
package postgresql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMapFields(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *postgresql.Instance
|
|
||||||
expected Model
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&postgresql.Instance{},
|
|
||||||
Model{
|
|
||||||
Id: types.StringValue("pid,iid"),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
PlanId: types.StringNull(),
|
|
||||||
Name: types.StringNull(),
|
|
||||||
CfGuid: types.StringNull(),
|
|
||||||
CfSpaceGuid: types.StringNull(),
|
|
||||||
DashboardUrl: types.StringNull(),
|
|
||||||
ImageUrl: types.StringNull(),
|
|
||||||
CfOrganizationGuid: types.StringNull(),
|
|
||||||
Parameters: types.ObjectNull(parametersTypes),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"simple_values",
|
|
||||||
&postgresql.Instance{
|
|
||||||
PlanId: utils.Ptr("plan"),
|
|
||||||
CfGuid: utils.Ptr("cf"),
|
|
||||||
CfSpaceGuid: utils.Ptr("space"),
|
|
||||||
DashboardUrl: utils.Ptr("dashboard"),
|
|
||||||
ImageUrl: utils.Ptr("image"),
|
|
||||||
InstanceId: utils.Ptr("iid"),
|
|
||||||
Name: utils.Ptr("name"),
|
|
||||||
CfOrganizationGuid: utils.Ptr("org"),
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"enable_monitoring": true,
|
|
||||||
"metrics_frequency": 1234,
|
|
||||||
"plugins": []string{
|
|
||||||
"plugin_1",
|
|
||||||
"plugin_2",
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{
|
|
||||||
Id: types.StringValue("pid,iid"),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
PlanId: types.StringValue("plan"),
|
|
||||||
Name: types.StringValue("name"),
|
|
||||||
CfGuid: types.StringValue("cf"),
|
|
||||||
CfSpaceGuid: types.StringValue("space"),
|
|
||||||
DashboardUrl: types.StringValue("dashboard"),
|
|
||||||
ImageUrl: types.StringValue("image"),
|
|
||||||
CfOrganizationGuid: types.StringValue("org"),
|
|
||||||
Parameters: types.ObjectValueMust(parametersTypes, map[string]attr.Value{
|
|
||||||
"enable_monitoring": types.BoolValue(true),
|
|
||||||
"metrics_frequency": types.Int64Value(1234),
|
|
||||||
"metrics_prefix": types.StringNull(),
|
|
||||||
"monitoring_instance_id": types.StringNull(),
|
|
||||||
"plugins": types.ListValueMust(types.StringType, []attr.Value{
|
|
||||||
types.StringValue("plugin_1"),
|
|
||||||
types.StringValue("plugin_2"),
|
|
||||||
types.StringValue(""),
|
|
||||||
}),
|
|
||||||
"sgw_acl": types.StringNull(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response",
|
|
||||||
nil,
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_resource_id",
|
|
||||||
&postgresql.Instance{},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"wrong_param_types_1",
|
|
||||||
&postgresql.Instance{
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"enable_monitoring": "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"wrong_param_types_2",
|
|
||||||
&postgresql.Instance{
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"metrics_frequency": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"wrong_param_types_3",
|
|
||||||
&postgresql.Instance{
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"metrics_frequency": 12.34,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"wrong_param_types_4",
|
|
||||||
&postgresql.Instance{
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"plugins": "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"wrong_param_types_5",
|
|
||||||
&postgresql.Instance{
|
|
||||||
Parameters: &map[string]interface{}{
|
|
||||||
"plugins": []bool{
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Model{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
|
||||||
state := &Model{
|
|
||||||
ProjectId: tt.expected.ProjectId,
|
|
||||||
InstanceId: tt.expected.InstanceId,
|
|
||||||
}
|
|
||||||
err := mapFields(tt.input, state)
|
|
||||||
if !tt.isValid && err == nil {
|
|
||||||
t.Fatalf("Should have failed")
|
|
||||||
}
|
|
||||||
if tt.isValid && err != nil {
|
|
||||||
t.Fatalf("Should not have failed: %v", err)
|
|
||||||
}
|
|
||||||
if tt.isValid {
|
|
||||||
diff := cmp.Diff(state, &tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToCreatePayload(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *Model
|
|
||||||
inputParameters *parametersModel
|
|
||||||
inputParametersPlugins *[]string
|
|
||||||
expected *postgresql.CreateInstancePayload
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&Model{},
|
|
||||||
¶metersModel{},
|
|
||||||
&[]string{},
|
|
||||||
&postgresql.CreateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
Plugins: &[]string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_values",
|
|
||||||
&Model{},
|
|
||||||
¶metersModel{},
|
|
||||||
nil,
|
|
||||||
&postgresql.CreateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
Plugins: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"simple_values",
|
|
||||||
&Model{
|
|
||||||
Name: types.StringValue("name"),
|
|
||||||
PlanId: types.StringValue("plan"),
|
|
||||||
},
|
|
||||||
¶metersModel{
|
|
||||||
EnableMonitoring: types.BoolValue(true),
|
|
||||||
MetricsFrequency: types.Int64Value(123),
|
|
||||||
MetricsPrefix: types.StringValue("prefix"),
|
|
||||||
MonitoringInstanceId: types.StringValue("monitoring"),
|
|
||||||
SgwAcl: types.StringValue("sgw"),
|
|
||||||
},
|
|
||||||
&[]string{
|
|
||||||
"plugin_1",
|
|
||||||
"plugin_2",
|
|
||||||
},
|
|
||||||
&postgresql.CreateInstancePayload{
|
|
||||||
InstanceName: utils.Ptr("name"),
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: utils.Ptr(true),
|
|
||||||
MetricsFrequency: utils.Ptr(int64(123)),
|
|
||||||
MetricsPrefix: utils.Ptr("prefix"),
|
|
||||||
MonitoringInstanceId: utils.Ptr("monitoring"),
|
|
||||||
Plugins: &[]string{
|
|
||||||
"plugin_1",
|
|
||||||
"plugin_2",
|
|
||||||
},
|
|
||||||
SgwAcl: utils.Ptr("sgw"),
|
|
||||||
},
|
|
||||||
PlanId: utils.Ptr("plan"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"null_fields_and_int_conversions",
|
|
||||||
&Model{
|
|
||||||
Name: types.StringValue(""),
|
|
||||||
PlanId: types.StringValue(""),
|
|
||||||
},
|
|
||||||
¶metersModel{
|
|
||||||
EnableMonitoring: types.BoolNull(),
|
|
||||||
MetricsFrequency: types.Int64Value(2123456789),
|
|
||||||
MetricsPrefix: types.StringNull(),
|
|
||||||
MonitoringInstanceId: types.StringNull(),
|
|
||||||
SgwAcl: types.StringNull(),
|
|
||||||
},
|
|
||||||
&[]string{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
&postgresql.CreateInstancePayload{
|
|
||||||
InstanceName: utils.Ptr(""),
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: nil,
|
|
||||||
MetricsFrequency: utils.Ptr(int64(2123456789)),
|
|
||||||
MetricsPrefix: nil,
|
|
||||||
MonitoringInstanceId: nil,
|
|
||||||
Plugins: &[]string{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
SgwAcl: nil,
|
|
||||||
},
|
|
||||||
PlanId: utils.Ptr(""),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_model",
|
|
||||||
nil,
|
|
||||||
¶metersModel{},
|
|
||||||
&[]string{},
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_parameters",
|
|
||||||
&Model{
|
|
||||||
Name: types.StringValue("name"),
|
|
||||||
PlanId: types.StringValue("plan"),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
&postgresql.CreateInstancePayload{
|
|
||||||
InstanceName: utils.Ptr("name"),
|
|
||||||
PlanId: utils.Ptr("plan"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
|
||||||
output, err := toCreatePayload(tt.input, tt.inputParameters, tt.inputParametersPlugins)
|
|
||||||
if !tt.isValid && err == nil {
|
|
||||||
t.Fatalf("Should have failed")
|
|
||||||
}
|
|
||||||
if tt.isValid && err != nil {
|
|
||||||
t.Fatalf("Should not have failed: %v", err)
|
|
||||||
}
|
|
||||||
if tt.isValid {
|
|
||||||
diff := cmp.Diff(output, tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToUpdatePayload(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *Model
|
|
||||||
inputParameters *parametersModel
|
|
||||||
inputParametersPlugins *[]string
|
|
||||||
expected *postgresql.PartialUpdateInstancePayload
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&Model{},
|
|
||||||
¶metersModel{},
|
|
||||||
&[]string{},
|
|
||||||
&postgresql.PartialUpdateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
Plugins: &[]string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"simple_values",
|
|
||||||
&Model{
|
|
||||||
PlanId: types.StringValue("plan"),
|
|
||||||
},
|
|
||||||
¶metersModel{
|
|
||||||
EnableMonitoring: types.BoolValue(true),
|
|
||||||
MetricsFrequency: types.Int64Value(123),
|
|
||||||
MetricsPrefix: types.StringValue("prefix"),
|
|
||||||
MonitoringInstanceId: types.StringValue("monitoring"),
|
|
||||||
SgwAcl: types.StringValue("sgw"),
|
|
||||||
},
|
|
||||||
&[]string{
|
|
||||||
"plugin_1",
|
|
||||||
"plugin_2",
|
|
||||||
},
|
|
||||||
&postgresql.PartialUpdateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: utils.Ptr(true),
|
|
||||||
MetricsFrequency: utils.Ptr(int64(123)),
|
|
||||||
MetricsPrefix: utils.Ptr("prefix"),
|
|
||||||
MonitoringInstanceId: utils.Ptr("monitoring"),
|
|
||||||
Plugins: &[]string{
|
|
||||||
"plugin_1",
|
|
||||||
"plugin_2",
|
|
||||||
},
|
|
||||||
SgwAcl: utils.Ptr("sgw"),
|
|
||||||
},
|
|
||||||
PlanId: utils.Ptr("plan"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"null_fields_and_int_conversions",
|
|
||||||
&Model{
|
|
||||||
PlanId: types.StringValue(""),
|
|
||||||
},
|
|
||||||
¶metersModel{
|
|
||||||
EnableMonitoring: types.BoolNull(),
|
|
||||||
MetricsFrequency: types.Int64Value(2123456789),
|
|
||||||
MetricsPrefix: types.StringNull(),
|
|
||||||
MonitoringInstanceId: types.StringNull(),
|
|
||||||
SgwAcl: types.StringNull(),
|
|
||||||
},
|
|
||||||
&[]string{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
&postgresql.PartialUpdateInstancePayload{
|
|
||||||
Parameters: &postgresql.InstanceParameters{
|
|
||||||
EnableMonitoring: nil,
|
|
||||||
MetricsFrequency: utils.Ptr(int64(2123456789)),
|
|
||||||
MetricsPrefix: nil,
|
|
||||||
MonitoringInstanceId: nil,
|
|
||||||
Plugins: &[]string{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
SgwAcl: nil,
|
|
||||||
},
|
|
||||||
PlanId: utils.Ptr(""),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_model",
|
|
||||||
nil,
|
|
||||||
¶metersModel{},
|
|
||||||
&[]string{},
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_parameters",
|
|
||||||
&Model{
|
|
||||||
PlanId: types.StringValue("plan"),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
&postgresql.PartialUpdateInstancePayload{
|
|
||||||
PlanId: utils.Ptr("plan"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.description, func(t *testing.T) {
|
|
||||||
output, err := toUpdatePayload(tt.input, tt.inputParameters, tt.inputParametersPlugins)
|
|
||||||
if !tt.isValid && err == nil {
|
|
||||||
t.Fatalf("Should have failed")
|
|
||||||
}
|
|
||||||
if tt.isValid && err != nil {
|
|
||||||
t.Fatalf("Should not have failed: %v", err)
|
|
||||||
}
|
|
||||||
if tt.isValid {
|
|
||||||
diff := cmp.Diff(output, tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,255 +0,0 @@
|
||||||
package postgresql_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
|
||||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresql/wait"
|
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
|
||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Instance resource data
|
|
||||||
var instanceResource = map[string]string{
|
|
||||||
"project_id": testutil.ProjectId,
|
|
||||||
"name": testutil.ResourceNameWithDateTime("postgresql"),
|
|
||||||
"plan_id": "57d40175-0f4c-4bcc-b52d-cf5d2ee9f5a7",
|
|
||||||
"plan_name": "stackit-qa-postgresql-1.4.10-single",
|
|
||||||
"version": "13",
|
|
||||||
"sgw_acl": "192.168.0.0/16",
|
|
||||||
"metrics_frequency": "34",
|
|
||||||
"plugins": "foo-bar",
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceConfig(acls, frequency, plugins string) string {
|
|
||||||
return fmt.Sprintf(`
|
|
||||||
%s
|
|
||||||
|
|
||||||
resource "stackit_postgresql_instance" "instance" {
|
|
||||||
project_id = "%s"
|
|
||||||
name = "%s"
|
|
||||||
plan_name = "%s"
|
|
||||||
version = "%s"
|
|
||||||
parameters = {
|
|
||||||
sgw_acl = "%s"
|
|
||||||
plugins = ["%s"]
|
|
||||||
# metrics_frequency = %s
|
|
||||||
# metrics_prefix = "pre"
|
|
||||||
# enable_monitoring = true
|
|
||||||
# monitoring_instance_id = "b9e38481-4f3d-4a28-8ed0-43fd32c024c7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "stackit_postgresql_credential" "credential" {
|
|
||||||
project_id = stackit_postgresql_instance.instance.project_id
|
|
||||||
instance_id = stackit_postgresql_instance.instance.instance_id
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
testutil.PostgreSQLProviderConfig(),
|
|
||||||
instanceResource["project_id"],
|
|
||||||
instanceResource["name"],
|
|
||||||
instanceResource["plan_name"],
|
|
||||||
instanceResource["version"],
|
|
||||||
acls,
|
|
||||||
plugins,
|
|
||||||
frequency,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
func TestAccPostgreSQLResource(t *testing.T) {
|
|
||||||
resource.Test(t, resource.TestCase{
|
|
||||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
|
||||||
CheckDestroy: testAccCheckPostgreSQLDestroy,
|
|
||||||
Steps: []resource.TestStep{
|
|
||||||
|
|
||||||
// Creation
|
|
||||||
{
|
|
||||||
Config: resourceConfig(instanceResource["sgw_acl"], instanceResource["metrics_frequency"], instanceResource["plugins"]),
|
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
|
||||||
// Instance data
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "project_id", instanceResource["project_id"]),
|
|
||||||
resource.TestCheckResourceAttrSet("stackit_postgresql_instance.instance", "instance_id"),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "plan_id", instanceResource["plan_id"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "plan_name", instanceResource["plan_name"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "version", instanceResource["version"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "name", instanceResource["name"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "parameters.sgw_acl", instanceResource["sgw_acl"]),
|
|
||||||
|
|
||||||
// Credential data
|
|
||||||
resource.TestCheckResourceAttrPair(
|
|
||||||
"stackit_postgresql_credential.credential", "project_id",
|
|
||||||
"stackit_postgresql_instance.instance", "project_id",
|
|
||||||
),
|
|
||||||
resource.TestCheckResourceAttrPair(
|
|
||||||
"stackit_postgresql_credential.credential", "instance_id",
|
|
||||||
"stackit_postgresql_instance.instance", "instance_id",
|
|
||||||
),
|
|
||||||
resource.TestCheckResourceAttrSet("stackit_postgresql_credential.credential", "credential_id"),
|
|
||||||
resource.TestCheckResourceAttrSet("stackit_postgresql_credential.credential", "host"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{ // Data source
|
|
||||||
Config: fmt.Sprintf(`
|
|
||||||
%s
|
|
||||||
|
|
||||||
data "stackit_postgresql_instance" "instance" {
|
|
||||||
project_id = stackit_postgresql_instance.instance.project_id
|
|
||||||
instance_id = stackit_postgresql_instance.instance.instance_id
|
|
||||||
}
|
|
||||||
|
|
||||||
data "stackit_postgresql_credential" "credential" {
|
|
||||||
project_id = stackit_postgresql_credential.credential.project_id
|
|
||||||
instance_id = stackit_postgresql_credential.credential.instance_id
|
|
||||||
credential_id = stackit_postgresql_credential.credential.credential_id
|
|
||||||
}`,
|
|
||||||
resourceConfig(instanceResource["sgw_acl"], instanceResource["metrics_frequency"], instanceResource["plugins"]),
|
|
||||||
),
|
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
|
||||||
// Instance data
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "project_id", instanceResource["project_id"]),
|
|
||||||
resource.TestCheckResourceAttrPair("stackit_postgresql_instance.instance", "instance_id",
|
|
||||||
"data.stackit_postgresql_instance.instance", "instance_id"),
|
|
||||||
resource.TestCheckResourceAttrPair("stackit_postgresql_credential.credential", "credential_id",
|
|
||||||
"data.stackit_postgresql_credential.credential", "credential_id"),
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "plan_id", instanceResource["plan_id"]),
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "name", instanceResource["name"]),
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "parameters.sgw_acl", instanceResource["sgw_acl"]),
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "parameters.plugins.#", "1"),
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_instance.instance", "parameters.plugins.0", instanceResource["plugins"]),
|
|
||||||
|
|
||||||
// Credential data
|
|
||||||
resource.TestCheckResourceAttr("data.stackit_postgresql_credential.credential", "project_id", instanceResource["project_id"]),
|
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_postgresql_credential.credential", "credential_id"),
|
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_postgresql_credential.credential", "host"),
|
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_postgresql_credential.credential", "port"),
|
|
||||||
resource.TestCheckResourceAttrSet("data.stackit_postgresql_credential.credential", "uri"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
// Import
|
|
||||||
{
|
|
||||||
ResourceName: "stackit_postgresql_instance.instance",
|
|
||||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
|
||||||
r, ok := s.RootModule().Resources["stackit_postgresql_instance.instance"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("couldn't find resource stackit_postgresql_instance.instance")
|
|
||||||
}
|
|
||||||
instanceId, ok := r.Primary.Attributes["instance_id"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("couldn't find attribute instance_id")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s,%s", testutil.ProjectId, instanceId), nil
|
|
||||||
},
|
|
||||||
ImportState: true,
|
|
||||||
ImportStateVerify: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ResourceName: "stackit_postgresql_credential.credential",
|
|
||||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
|
||||||
r, ok := s.RootModule().Resources["stackit_postgresql_credential.credential"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("couldn't find resource stackit_postgresql_credential.credential")
|
|
||||||
}
|
|
||||||
instanceId, ok := r.Primary.Attributes["instance_id"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("couldn't find attribute instance_id")
|
|
||||||
}
|
|
||||||
credentialId, ok := r.Primary.Attributes["credential_id"]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("couldn't find attribute credential_id")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, credentialId), nil
|
|
||||||
},
|
|
||||||
ImportState: true,
|
|
||||||
ImportStateVerify: true,
|
|
||||||
},
|
|
||||||
// Update
|
|
||||||
{
|
|
||||||
Config: resourceConfig(instanceResource["sgw_acl"], fmt.Sprintf("%s0", instanceResource["metrics_frequency"]), fmt.Sprintf("%s-baz", instanceResource["plugins"])),
|
|
||||||
Check: resource.ComposeAggregateTestCheckFunc(
|
|
||||||
// Instance data
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "project_id", instanceResource["project_id"]),
|
|
||||||
resource.TestCheckResourceAttrSet("stackit_postgresql_instance.instance", "instance_id"),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "plan_id", instanceResource["plan_id"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "plan_name", instanceResource["plan_name"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "version", instanceResource["version"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "name", instanceResource["name"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "parameters.sgw_acl", instanceResource["sgw_acl"]),
|
|
||||||
resource.TestCheckResourceAttr("stackit_postgresql_instance.instance", "parameters.plugins.0", fmt.Sprintf("%s-baz", instanceResource["plugins"])),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
// Deletion is done by the framework implicitly
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccCheckPostgreSQLDestroy(s *terraform.State) error {
|
|
||||||
ctx := context.Background()
|
|
||||||
var client *postgresql.APIClient
|
|
||||||
var err error
|
|
||||||
if testutil.PostgreSQLCustomEndpoint == "" {
|
|
||||||
client, err = postgresql.NewAPIClient()
|
|
||||||
} else {
|
|
||||||
client, err = postgresql.NewAPIClient(
|
|
||||||
config.WithEndpoint(testutil.PostgreSQLCustomEndpoint),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("creating client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
instancesToDestroy := []string{}
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
|
||||||
if rs.Type != "stackit_postgresql_instance" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// instance terraform ID: "[project_id],[instance_id]"
|
|
||||||
instanceId := strings.Split(rs.Primary.ID, core.Separator)[1]
|
|
||||||
instancesToDestroy = append(instancesToDestroy, instanceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
instancesResp, err := client.ListInstances(ctx, testutil.ProjectId).Execute()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting instancesResp: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
instances := *instancesResp.Instances
|
|
||||||
for i := range instances {
|
|
||||||
if instances[i].InstanceId == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if utils.Contains(instancesToDestroy, *instances[i].InstanceId) {
|
|
||||||
if !checkInstanceDeleteSuccess(&instances[i]) {
|
|
||||||
err := client.DeleteInstanceExecute(ctx, testutil.ProjectId, *instances[i].InstanceId)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("destroying instance %s during CheckDestroy: %w", *instances[i].InstanceId, err)
|
|
||||||
}
|
|
||||||
_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, *instances[i].InstanceId).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("destroying instance %s during CheckDestroy: waiting for deletion %w", *instances[i].InstanceId, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkInstanceDeleteSuccess(i *postgresql.Instance) bool {
|
|
||||||
if *i.LastOperation.Type != wait.InstanceTypeDelete {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if *i.LastOperation.Type == wait.InstanceTypeDelete {
|
|
||||||
if *i.LastOperation.State != wait.InstanceStateSuccess {
|
|
||||||
return false
|
|
||||||
} else if strings.Contains(*i.LastOperation.Description, "DeleteFailed") || strings.Contains(*i.LastOperation.Description, "failed") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue