chore: changed and refactored providers (#36)
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Reviewed-on: #36
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
This commit is contained in:
parent
b1b359f436
commit
de019908d2
70 changed files with 6250 additions and 2608 deletions
|
|
@ -5,18 +5,17 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
postgresflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database/datasources_gen"
|
||||
postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
)
|
||||
|
||||
|
|
@ -30,6 +29,12 @@ func NewDatabaseDataSource() datasource.DataSource {
|
|||
return &databaseDataSource{}
|
||||
}
|
||||
|
||||
// dataSourceModel maps the data source schema data.
|
||||
type dataSourceModel struct {
|
||||
postgresflexalpha2.DatabaseModel
|
||||
TerraformID types.String `tfsdk:"id"`
|
||||
}
|
||||
|
||||
// databaseDataSource is the data source implementation.
|
||||
type databaseDataSource struct {
|
||||
client *postgresflexalpha.APIClient
|
||||
|
|
@ -66,132 +71,46 @@ func (r *databaseDataSource) Configure(
|
|||
}
|
||||
|
||||
// Schema defines the schema for the data source.
|
||||
func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`,`database_id`\".",
|
||||
"database_id": "Database ID.",
|
||||
"instance_id": "ID of the Postgres Flex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Database name.",
|
||||
"owner": "Username of the database owner.",
|
||||
"region": "The resource region. If not defined, the provider region is used.",
|
||||
func (r *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
|
||||
s := postgresflexalpha2.DatabaseDataSourceSchema(ctx)
|
||||
s.Attributes["id"] = schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`," +
|
||||
"`database_id`\\\".\",",
|
||||
Computed: true,
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"database_id": schema.Int64Attribute{
|
||||
Description: descriptions["database_id"],
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Description: descriptions["owner"],
|
||||
Computed: true,
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
// the region cannot be found, so it has to be passed
|
||||
Optional: true,
|
||||
Description: descriptions["region"],
|
||||
},
|
||||
},
|
||||
}
|
||||
resp.Schema = s
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
// Read fetches the data for the data source.
|
||||
func (r *databaseDataSource) Read(
|
||||
ctx context.Context,
|
||||
req datasource.ReadRequest,
|
||||
resp *datasource.ReadResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model dataSourceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// validation for exactly one of database_id or name
|
||||
isIdSet := !model.DatabaseId.IsNull() && !model.DatabaseId.IsUnknown()
|
||||
isNameSet := !model.Name.IsNull() && !model.Name.IsUnknown()
|
||||
|
||||
if (isIdSet && isNameSet) || (!isIdSet && !isNameSet) {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Invalid configuration", "Exactly one of 'database_id' or 'name' must be specified.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
databaseId := model.DatabaseId.ValueInt64()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var databaseResp *postgresflexalpha.ListDatabase
|
||||
var err error
|
||||
|
||||
if isIdSet {
|
||||
databaseId := model.DatabaseId.ValueInt64()
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
databaseResp, err = getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
|
||||
} else {
|
||||
databaseName := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", databaseName)
|
||||
databaseResp, err = getDatabaseByName(ctx, r.client, projectId, region, instanceId, databaseName)
|
||||
databaseResp, err := r.getDatabaseByNameOrID(ctx, &model, projectId, region, instanceId, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading database",
|
||||
fmt.Sprintf(
|
||||
"Database with ID %q or instance with ID %q does not exist in project %q.",
|
||||
databaseId,
|
||||
instanceId,
|
||||
projectId,
|
||||
),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
|
@ -218,3 +137,60 @@ func (r *databaseDataSource) Read(
|
|||
}
|
||||
tflog.Info(ctx, "Postgres Flex database read")
|
||||
}
|
||||
|
||||
// getDatabaseByNameOrID retrieves a single database by ensuring either a unique ID or name is provided.
|
||||
func (r *databaseDataSource) getDatabaseByNameOrID(
|
||||
ctx context.Context,
|
||||
model *dataSourceModel,
|
||||
projectId, region, instanceId string,
|
||||
diags *diag.Diagnostics,
|
||||
) (*postgresflexalpha.ListDatabase, error) {
|
||||
isIdSet := !model.DatabaseId.IsNull() && !model.DatabaseId.IsUnknown()
|
||||
isNameSet := !model.Name.IsNull() && !model.Name.IsUnknown()
|
||||
|
||||
if (isIdSet && isNameSet) || (!isIdSet && !isNameSet) {
|
||||
diags.AddError(
|
||||
"Invalid configuration",
|
||||
"Exactly one of 'id' or 'name' must be specified.",
|
||||
)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if isIdSet {
|
||||
databaseId := model.DatabaseId.ValueInt64()
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
return getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
|
||||
}
|
||||
|
||||
databaseName := model.Name.ValueString()
|
||||
ctx = tflog.SetField(ctx, "name", databaseName)
|
||||
return getDatabaseByName(ctx, r.client, projectId, region, instanceId, databaseName)
|
||||
}
|
||||
|
||||
// handleReadError centralizes API error handling for the Read operation.
|
||||
func handleReadError(ctx context.Context, diags *diag.Diagnostics, err error, projectId, instanceId string) {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
diags,
|
||||
err,
|
||||
"Reading database",
|
||||
fmt.Sprintf(
|
||||
"Could not retrieve database for instance %q in project %q.",
|
||||
instanceId,
|
||||
projectId,
|
||||
),
|
||||
map[int]string{
|
||||
http.StatusBadRequest: fmt.Sprintf(
|
||||
"Invalid request parameters for project %q and instance %q.",
|
||||
projectId,
|
||||
instanceId,
|
||||
),
|
||||
http.StatusNotFound: fmt.Sprintf(
|
||||
"Database, instance %q, or project %q not found.",
|
||||
instanceId,
|
||||
projectId,
|
||||
),
|
||||
http.StatusForbidden: fmt.Sprintf("Forbidden access to project %q.", projectId),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func DatabaseDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"database_id": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "The ID of the database.",
|
||||
MarkdownDescription: "The ID of the database.",
|
||||
},
|
||||
"tf_original_api_id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The owner of the database.",
|
||||
MarkdownDescription: "The owner of the database.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseModel struct {
|
||||
DatabaseId types.Int64 `tfsdk:"database_id"`
|
||||
Id types.Int64 `tfsdk:"tf_original_api_id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package postgresflexalpha
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
)
|
||||
|
|
@ -79,3 +80,12 @@ func getDatabase(
|
|||
|
||||
return nil, fmt.Errorf("database not found for instance %s", instanceId)
|
||||
}
|
||||
|
||||
// cleanString removes leading and trailing quotes which are sometimes returned by the API.
|
||||
func cleanString(s *string) *string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
res := strings.Trim(*s, "\"")
|
||||
return &res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
)
|
||||
|
|
@ -12,8 +13,8 @@ type mockRequest struct {
|
|||
executeFunc func() (*postgresflex.ListDatabasesResponse, error)
|
||||
}
|
||||
|
||||
func (m *mockRequest) Page(_ int64) postgresflex.ApiListDatabasesRequestRequest { return m }
|
||||
func (m *mockRequest) Size(_ int64) postgresflex.ApiListDatabasesRequestRequest { return m }
|
||||
func (m *mockRequest) Page(_ int32) postgresflex.ApiListDatabasesRequestRequest { return m }
|
||||
func (m *mockRequest) Size(_ int32) postgresflex.ApiListDatabasesRequestRequest { return m }
|
||||
func (m *mockRequest) Sort(_ postgresflex.DatabaseSort) postgresflex.ApiListDatabasesRequestRequest {
|
||||
return m
|
||||
}
|
||||
|
|
@ -176,21 +177,56 @@ func TestGetDatabase(t *testing.T) {
|
|||
}
|
||||
|
||||
if (errDB != nil) != tt.wantErr {
|
||||
t.Errorf("getDatabase() error = %v, wantErr %v", errDB, tt.wantErr)
|
||||
t.Errorf("getDatabaseByNameOrID() error = %v, wantErr %v", errDB, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr && tt.wantDbName != "" && actual != nil {
|
||||
if *actual.Name != tt.wantDbName {
|
||||
t.Errorf("getDatabase() got name = %v, want %v", *actual.Name, tt.wantDbName)
|
||||
t.Errorf("getDatabaseByNameOrID() got name = %v, want %v", *actual.Name, tt.wantDbName)
|
||||
}
|
||||
}
|
||||
|
||||
if !tt.wantErr && tt.wantDbId != 0 && actual != nil {
|
||||
if *actual.Id != tt.wantDbId {
|
||||
t.Errorf("getDatabase() got id = %v, want %v", *actual.Id, tt.wantDbId)
|
||||
t.Errorf("getDatabaseByNameOrID() got id = %v, want %v", *actual.Id, tt.wantDbId)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanString(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
given *string
|
||||
expected *string
|
||||
}{
|
||||
{
|
||||
name: "should remove quotes",
|
||||
given: utils.Ptr("\"quoted\""),
|
||||
expected: utils.Ptr("quoted"),
|
||||
},
|
||||
{
|
||||
name: "should handle nil",
|
||||
given: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "should not change unquoted string",
|
||||
given: utils.Ptr("unquoted"),
|
||||
expected: utils.Ptr("unquoted"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
actual := cleanString(tc.given)
|
||||
if diff := cmp.Diff(tc.expected, actual); diff != "" {
|
||||
t.Errorf("string mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
)
|
||||
|
||||
// mapFields maps fields from a ListDatabase API response to a resourceModel for the data source.
|
||||
func mapFields(
|
||||
source *postgresflexalpha.ListDatabase,
|
||||
model *dataSourceModel,
|
||||
region string,
|
||||
) error {
|
||||
if source == nil {
|
||||
return fmt.Errorf("response is nil")
|
||||
}
|
||||
if source.Id == nil || *source.Id == 0 {
|
||||
return fmt.Errorf("id not present")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model given is nil")
|
||||
}
|
||||
|
||||
var databaseId int64
|
||||
if model.DatabaseId.ValueInt64() != 0 {
|
||||
databaseId = model.DatabaseId.ValueInt64()
|
||||
} else if source.Id != nil {
|
||||
databaseId = *source.Id
|
||||
} else {
|
||||
return fmt.Errorf("database id not present")
|
||||
}
|
||||
|
||||
model.Id = types.Int64Value(databaseId)
|
||||
model.DatabaseId = types.Int64Value(databaseId)
|
||||
model.Name = types.StringValue(source.GetName())
|
||||
model.Owner = types.StringPointerValue(cleanString(source.Owner))
|
||||
model.Region = types.StringValue(region)
|
||||
model.ProjectId = types.StringValue(model.ProjectId.ValueString())
|
||||
model.InstanceId = types.StringValue(model.InstanceId.ValueString())
|
||||
model.TerraformID = utils.BuildInternalTerraformId(
|
||||
model.ProjectId.ValueString(),
|
||||
region,
|
||||
model.InstanceId.ValueString(),
|
||||
strconv.FormatInt(databaseId, 10),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapResourceFields maps fields from a ListDatabase API response to a resourceModel for the resource.
|
||||
func mapResourceFields(source *postgresflexalpha.ListDatabase, model *resourceModel) error {
|
||||
if source == nil {
|
||||
return fmt.Errorf("response is nil")
|
||||
}
|
||||
if source.Id == nil || *source.Id == 0 {
|
||||
return fmt.Errorf("id not present")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var databaseId int64
|
||||
if model.Id.ValueInt64() != 0 {
|
||||
databaseId = model.Id.ValueInt64()
|
||||
} else if source.Id != nil {
|
||||
databaseId = *source.Id
|
||||
} else {
|
||||
return fmt.Errorf("database id not present")
|
||||
}
|
||||
|
||||
model.Id = types.Int64Value(databaseId)
|
||||
model.DatabaseId = types.Int64Value(databaseId)
|
||||
model.Name = types.StringValue(source.GetName())
|
||||
model.Owner = types.StringPointerValue(cleanString(source.Owner))
|
||||
return nil
|
||||
}
|
||||
|
||||
// toCreatePayload converts the resource model to an API create payload.
|
||||
func toCreatePayload(model *resourceModel) (*postgresflexalpha.CreateDatabaseRequestPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
|
||||
return &postgresflexalpha.CreateDatabaseRequestPayload{
|
||||
Name: model.Name.ValueStringPointer(),
|
||||
Owner: model.Owner.ValueStringPointer(),
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
datasource "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database/datasources_gen"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
type given struct {
|
||||
source *postgresflexalpha.ListDatabase
|
||||
model *dataSourceModel
|
||||
region string
|
||||
}
|
||||
type expected struct {
|
||||
model *dataSourceModel
|
||||
err bool
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
given given
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
name: "should map fields correctly",
|
||||
given: given{
|
||||
source: &postgresflexalpha.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
Name: utils.Ptr("my-db"),
|
||||
Owner: utils.Ptr("\"my-owner\""),
|
||||
},
|
||||
model: &dataSourceModel{},
|
||||
region: "eu01",
|
||||
},
|
||||
expected: expected{
|
||||
model: &dataSourceModel{
|
||||
DatabaseModel: datasource.DatabaseModel{
|
||||
Id: types.Int64Value(1),
|
||||
Name: types.StringValue("my-db"),
|
||||
Owner: types.StringValue("my-owner"),
|
||||
Region: types.StringValue("eu01"),
|
||||
DatabaseId: types.Int64Value(1),
|
||||
InstanceId: types.StringValue("my-instance"),
|
||||
ProjectId: types.StringValue("my-project"),
|
||||
},
|
||||
TerraformID: types.StringValue("my-project,eu01,my-instance,1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should preserve existing model ID",
|
||||
given: given{
|
||||
source: &postgresflexalpha.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
Name: utils.Ptr("my-db"),
|
||||
},
|
||||
model: &dataSourceModel{
|
||||
DatabaseModel: datasource.DatabaseModel{
|
||||
Id: types.Int64Value(1),
|
||||
ProjectId: types.StringValue("my-project"),
|
||||
InstanceId: types.StringValue("my-instance"),
|
||||
},
|
||||
},
|
||||
region: "eu01",
|
||||
},
|
||||
expected: expected{
|
||||
model: &dataSourceModel{
|
||||
DatabaseModel: datasource.DatabaseModel{
|
||||
Id: types.Int64Value(1),
|
||||
Name: types.StringValue("my-db"),
|
||||
Owner: types.StringNull(), DatabaseId: types.Int64Value(1),
|
||||
Region: types.StringValue("eu01"),
|
||||
InstanceId: types.StringValue("my-instance"),
|
||||
ProjectId: types.StringValue("my-project"),
|
||||
},
|
||||
TerraformID: types.StringValue("my-project,eu01,my-instance,1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should fail on nil source",
|
||||
given: given{
|
||||
source: nil,
|
||||
model: &dataSourceModel{},
|
||||
},
|
||||
expected: expected{err: true},
|
||||
},
|
||||
{
|
||||
name: "should fail on nil source ID",
|
||||
given: given{
|
||||
source: &postgresflexalpha.ListDatabase{Id: nil},
|
||||
model: &dataSourceModel{},
|
||||
},
|
||||
expected: expected{err: true},
|
||||
},
|
||||
{
|
||||
name: "should fail on nil model",
|
||||
given: given{
|
||||
source: &postgresflexalpha.ListDatabase{Id: utils.Ptr(int64(1))},
|
||||
model: nil,
|
||||
},
|
||||
expected: expected{err: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
err := mapFields(tc.given.source, tc.given.model, tc.given.region)
|
||||
if (err != nil) != tc.expected.err {
|
||||
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||
}
|
||||
if err == nil {
|
||||
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapResourceFields(t *testing.T) {
|
||||
type given struct {
|
||||
source *postgresflexalpha.ListDatabase
|
||||
model *resourceModel
|
||||
}
|
||||
type expected struct {
|
||||
model *resourceModel
|
||||
err bool
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
given given
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
name: "should map fields correctly",
|
||||
given: given{
|
||||
source: &postgresflexalpha.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
Name: utils.Ptr("my-db"),
|
||||
Owner: utils.Ptr("\"my-owner\""),
|
||||
},
|
||||
model: &resourceModel{},
|
||||
},
|
||||
expected: expected{
|
||||
model: &resourceModel{
|
||||
Id: types.Int64Value(1),
|
||||
Name: types.StringValue("my-db"),
|
||||
Owner: types.StringValue("my-owner"),
|
||||
DatabaseId: types.Int64Value(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should fail on nil source",
|
||||
given: given{
|
||||
source: nil,
|
||||
model: &resourceModel{},
|
||||
},
|
||||
expected: expected{err: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
err := mapResourceFields(tc.given.source, tc.given.model)
|
||||
if (err != nil) != tc.expected.err {
|
||||
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||
}
|
||||
if err == nil {
|
||||
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
type given struct {
|
||||
model *resourceModel
|
||||
}
|
||||
type expected struct {
|
||||
payload *postgresflexalpha.CreateDatabaseRequestPayload
|
||||
err bool
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
given given
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
name: "should convert model to payload",
|
||||
given: given{
|
||||
model: &resourceModel{
|
||||
Name: types.StringValue("my-db"),
|
||||
Owner: types.StringValue("my-owner"),
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
payload: &postgresflexalpha.CreateDatabaseRequestPayload{
|
||||
Name: utils.Ptr("my-db"),
|
||||
Owner: utils.Ptr("my-owner"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should fail on nil model",
|
||||
given: given{model: nil},
|
||||
expected: expected{err: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(
|
||||
tc.name, func(t *testing.T) {
|
||||
actual, err := toCreatePayload(tc.given.model)
|
||||
if (err != nil) != tc.expected.err {
|
||||
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||
}
|
||||
if err == nil {
|
||||
if diff := cmp.Diff(tc.expected.payload, actual); diff != "" {
|
||||
t.Errorf("payload mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
fields:
|
||||
- name: 'id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'database_id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
|
||||
- name: 'instance_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'project_id'
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
- 'UseStateForUnknown'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
|
||||
- name: 'name'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
|
||||
- name: 'region'
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
|
@ -2,70 +2,73 @@ package postgresflexalpha
|
|||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
postgresflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database/resources_gen"
|
||||
postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
_ resource.Resource = &databaseResource{}
|
||||
_ resource.ResourceWithConfigure = &databaseResource{}
|
||||
_ resource.ResourceWithImportState = &databaseResource{}
|
||||
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
||||
)
|
||||
_ resource.ResourceWithIdentity = &databaseResource{}
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
DatabaseId types.Int64 `tfsdk:"database_id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
// Define errors
|
||||
errDatabaseNotFound = errors.New("database not found")
|
||||
|
||||
// Error message constants
|
||||
extractErrorSummary = "extracting failed"
|
||||
extractErrorMessage = "Extracting identity data: %v"
|
||||
)
|
||||
|
||||
// NewDatabaseResource is a helper function to simplify the provider implementation.
|
||||
func NewDatabaseResource() resource.Resource {
|
||||
return &databaseResource{}
|
||||
}
|
||||
|
||||
// resourceModel describes the resource data model.
|
||||
type resourceModel = postgresflexalpha2.DatabaseModel
|
||||
|
||||
// DatabaseResourceIdentityModel describes the resource's identity attributes.
|
||||
type DatabaseResourceIdentityModel struct {
|
||||
ProjectID types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
InstanceID types.String `tfsdk:"instance_id"`
|
||||
DatabaseID types.Int64 `tfsdk:"database_id"`
|
||||
}
|
||||
|
||||
// databaseResource is the resource implementation.
|
||||
type databaseResource struct {
|
||||
client *postgresflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
// ModifyPlan adjusts the plan to set the correct region.
|
||||
func (r *databaseResource) ModifyPlan(
|
||||
ctx context.Context,
|
||||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel Model
|
||||
var configModel resourceModel
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
|
|
@ -75,7 +78,7 @@ func (r *databaseResource) ModifyPlan(
|
|||
return
|
||||
}
|
||||
|
||||
var planModel Model
|
||||
var planModel resourceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -117,85 +120,46 @@ func (r *databaseResource) Configure(
|
|||
tflog.Info(ctx, "Postgres Flex database client configured")
|
||||
}
|
||||
|
||||
//go:embed planModifiers.yaml
|
||||
var modifiersFileByte []byte
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *databaseResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`,`database_id`\".",
|
||||
"database_id": "Database ID.",
|
||||
"instance_id": "ID of the Postgres Flex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Database name.",
|
||||
"owner": "Username of the database owner.",
|
||||
"region": "The resource region. If not defined, the provider region is used.",
|
||||
func (r *databaseResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
s := postgresflexalpha2.DatabaseResourceSchema(ctx)
|
||||
|
||||
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
err = utils.AddPlanModifiersToResourceSchema(fields, &s)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = s
|
||||
}
|
||||
|
||||
// IdentitySchema defines the schema for the resource's identity attributes.
|
||||
func (r *databaseResource) IdentitySchema(
|
||||
_ context.Context,
|
||||
_ resource.IdentitySchemaRequest,
|
||||
response *resource.IdentitySchemaResponse,
|
||||
) {
|
||||
response.IdentitySchema = identityschema.Schema{
|
||||
Attributes: map[string]identityschema.Attribute{
|
||||
"project_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true,
|
||||
},
|
||||
"database_id": schema.Int64Attribute{
|
||||
Description: descriptions["database_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.Int64{},
|
||||
"region": identityschema.StringAttribute{
|
||||
RequiredForImport: true,
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
"instance_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true,
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile("^[a-z]([a-z0-9]*)?$"),
|
||||
"must start with a letter, must have lower case letters or numbers",
|
||||
),
|
||||
},
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Description: descriptions["owner"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{},
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
// must be computed to allow for storing the override value from the provider
|
||||
Computed: true,
|
||||
Description: descriptions["region"],
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
"database_id": identityschema.Int64Attribute{
|
||||
RequiredForImport: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -207,18 +171,26 @@ func (r *databaseResource) Create(
|
|||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model resourceModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.ProjectID.ValueString()
|
||||
instanceId := identityData.InstanceID.ValueString()
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
|
@ -272,7 +244,7 @@ func (r *databaseResource) Create(
|
|||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(database, &model, region)
|
||||
err = mapResourceFields(database, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -282,9 +254,21 @@ func (r *databaseResource) Create(
|
|||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Set data returned by API in identity
|
||||
identity := DatabaseResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
InstanceID: types.StringValue(instanceId),
|
||||
DatabaseID: types.Int64Value(databaseId),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
|
@ -297,23 +281,36 @@ func (r *databaseResource) Read(
|
|||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model resourceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
databaseId := model.DatabaseId.ValueInt64()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
projectId, instanceId, region, databaseId, errExt := r.extractIdentityData(model, identityData)
|
||||
if errExt != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
extractErrorSummary,
|
||||
fmt.Sprintf(extractErrorMessage, errExt),
|
||||
)
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
|
||||
databaseResp, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId)
|
||||
if err != nil {
|
||||
|
|
@ -329,7 +326,7 @@ func (r *databaseResource) Read(
|
|||
ctx = core.LogResponse(ctx)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(databaseResp, &model, region)
|
||||
err = mapResourceFields(databaseResp, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -355,32 +352,45 @@ func (r *databaseResource) Update(
|
|||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) {
|
||||
var model Model
|
||||
var model resourceModel
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
databaseId64 := model.DatabaseId.ValueInt64()
|
||||
projectId, instanceId, region, databaseId64, errExt := r.extractIdentityData(model, identityData)
|
||||
if errExt != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
extractErrorSummary,
|
||||
fmt.Sprintf(extractErrorMessage, errExt),
|
||||
)
|
||||
}
|
||||
|
||||
if databaseId64 > math.MaxInt32 {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (databaseId)")
|
||||
return
|
||||
}
|
||||
databaseId := int32(databaseId64)
|
||||
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
|
||||
// Retrieve values from state
|
||||
var stateModel Model
|
||||
var stateModel resourceModel
|
||||
diags = req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -420,7 +430,7 @@ func (r *databaseResource) Update(
|
|||
ctx = core.LogResponse(ctx)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFieldsUpdatePartially(res, &model, region)
|
||||
err = mapResourceFields(res.Database, &model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
|
|
@ -445,29 +455,41 @@ func (r *databaseResource) Delete(
|
|||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model resourceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
databaseId64 := model.DatabaseId.ValueInt64()
|
||||
projectId, instanceId, region, databaseId64, errExt := r.extractIdentityData(model, identityData)
|
||||
if errExt != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
extractErrorSummary,
|
||||
fmt.Sprintf(extractErrorMessage, errExt),
|
||||
)
|
||||
}
|
||||
|
||||
if databaseId64 > math.MaxInt32 {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (databaseId)")
|
||||
return
|
||||
}
|
||||
databaseId := int32(databaseId64)
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
|
||||
// Delete existing record set
|
||||
err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseId)
|
||||
|
|
@ -481,95 +503,118 @@ func (r *databaseResource) Delete(
|
|||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
|
||||
// The expected import identifier format is: [project_id],[region],[instance_id],[database_id]
|
||||
func (r *databaseResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf(
|
||||
"Expected import identifier with format [project_id],[region],[instance_id],[database_id], got %q",
|
||||
req.ID,
|
||||
),
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
if req.ID != "" {
|
||||
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf(
|
||||
"Expected import identifier with format [project_id],[region],[instance_id],[database_id], got %q",
|
||||
req.ID,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
databaseId, err := strconv.ParseInt(idParts[3], 10, 64)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf("Invalid database_id format: %q. It must be a valid integer.", idParts[3]),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_id"), databaseId)...)
|
||||
|
||||
core.LogAndAddWarning(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Postgresflex database imported with empty password",
|
||||
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
|
||||
)
|
||||
|
||||
tflog.Info(ctx, "Postgres Flex database state imported")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_id"), idParts[3])...)
|
||||
core.LogAndAddWarning(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Postgresflex database imported with empty password",
|
||||
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
|
||||
)
|
||||
// If no ID is provided, attempt to read identity attributes from the import configuration
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.Region.ValueString()
|
||||
instanceId := identityData.InstanceID.ValueString()
|
||||
databaseId := identityData.DatabaseID.ValueInt64()
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), projectId)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), region)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_id"), databaseId)...)
|
||||
|
||||
tflog.Info(ctx, "Postgres Flex database state imported")
|
||||
}
|
||||
|
||||
func mapFields(resp *postgresflexalpha.ListDatabase, model *Model, region string) error {
|
||||
if resp == nil {
|
||||
return fmt.Errorf("response is nil")
|
||||
}
|
||||
if resp.Id == nil || *resp.Id == 0 {
|
||||
return fmt.Errorf("id not present")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var databaseId int64
|
||||
if model.DatabaseId.ValueInt64() != 0 {
|
||||
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
|
||||
func (r *databaseResource) extractIdentityData(
|
||||
model resourceModel,
|
||||
identity DatabaseResourceIdentityModel,
|
||||
) (projectId, region, instanceId string, databaseId int64, err error) {
|
||||
if !model.DatabaseId.IsNull() && !model.DatabaseId.IsUnknown() {
|
||||
databaseId = model.DatabaseId.ValueInt64()
|
||||
} else if resp.Id != nil {
|
||||
databaseId = *resp.Id
|
||||
} else {
|
||||
return fmt.Errorf("database id not present")
|
||||
}
|
||||
model.Id = utils.BuildInternalTerraformId(
|
||||
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(databaseId, 10),
|
||||
)
|
||||
model.DatabaseId = types.Int64Value(databaseId)
|
||||
model.Name = types.StringPointerValue(resp.Name)
|
||||
model.Region = types.StringValue(region)
|
||||
model.Owner = types.StringPointerValue(cleanString(resp.Owner))
|
||||
return nil
|
||||
}
|
||||
|
||||
func mapFieldsUpdatePartially(
|
||||
res *postgresflexalpha.UpdateDatabasePartiallyResponse,
|
||||
model *Model,
|
||||
region string,
|
||||
) error {
|
||||
if res == nil {
|
||||
return fmt.Errorf("response is nil")
|
||||
}
|
||||
return mapFields(res.Database, model, region)
|
||||
}
|
||||
|
||||
func cleanString(s *string) *string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
res := strings.Trim(*s, "\"")
|
||||
return &res
|
||||
}
|
||||
|
||||
func toCreatePayload(model *Model) (*postgresflexalpha.CreateDatabaseRequestPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
if identity.DatabaseID.IsNull() || identity.DatabaseID.IsUnknown() {
|
||||
return "", "", "", 0, fmt.Errorf("database_id not found in config")
|
||||
}
|
||||
databaseId = identity.DatabaseID.ValueInt64()
|
||||
}
|
||||
|
||||
return &postgresflexalpha.CreateDatabaseRequestPayload{
|
||||
Name: model.Name.ValueStringPointer(),
|
||||
Owner: model.Owner.ValueStringPointer(),
|
||||
}, nil
|
||||
}
|
||||
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
||||
projectId = model.ProjectId.ValueString()
|
||||
} else {
|
||||
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
|
||||
return "", "", "", 0, fmt.Errorf("project_id not found in config")
|
||||
}
|
||||
projectId = identity.ProjectID.ValueString()
|
||||
}
|
||||
|
||||
var errDatabaseNotFound = errors.New("database not found")
|
||||
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
||||
region = r.providerData.GetRegionWithOverride(model.Region)
|
||||
} else {
|
||||
if identity.Region.IsNull() || identity.Region.IsUnknown() {
|
||||
return "", "", "", 0, fmt.Errorf("region not found in config")
|
||||
}
|
||||
region = r.providerData.GetRegionWithOverride(identity.Region)
|
||||
}
|
||||
|
||||
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
|
||||
instanceId = model.InstanceId.ValueString()
|
||||
} else {
|
||||
if identity.InstanceID.IsNull() || identity.InstanceID.IsUnknown() {
|
||||
return "", "", "", 0, fmt.Errorf("instance_id not found in config")
|
||||
}
|
||||
instanceId = identity.InstanceID.ValueString()
|
||||
}
|
||||
return projectId, region, instanceId, databaseId, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,232 +0,0 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
const testRegion = "region"
|
||||
tests := []struct {
|
||||
description string
|
||||
input *postgresflex.ListDatabase
|
||||
region string
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
&postgresflex.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
},
|
||||
testRegion,
|
||||
Model{
|
||||
Id: types.StringValue("pid,region,iid,1"),
|
||||
DatabaseId: types.Int64Value(int64(1)),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringNull(),
|
||||
Owner: types.StringNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
&postgresflex.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
Name: utils.Ptr("dbname"),
|
||||
Owner: utils.Ptr("username"),
|
||||
},
|
||||
testRegion,
|
||||
Model{
|
||||
Id: types.StringValue("pid,region,iid,1"),
|
||||
DatabaseId: types.Int64Value(int64(1)),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue("dbname"),
|
||||
Owner: types.StringValue("username"),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"null_fields_and_int_conversions",
|
||||
&postgresflex.ListDatabase{
|
||||
Id: utils.Ptr(int64(1)),
|
||||
Name: utils.Ptr(""),
|
||||
Owner: utils.Ptr(""),
|
||||
},
|
||||
testRegion,
|
||||
Model{
|
||||
Id: types.StringValue("pid,region,iid,1"),
|
||||
DatabaseId: types.Int64Value(int64(1)),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue(""),
|
||||
Owner: types.StringValue(""),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_response",
|
||||
nil,
|
||||
testRegion,
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"empty_response",
|
||||
&postgresflex.ListDatabase{},
|
||||
testRegion,
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
&postgresflex.ListDatabase{
|
||||
Id: utils.Ptr(int64(0)),
|
||||
Name: utils.Ptr("dbname"),
|
||||
Owner: utils.Ptr("username"),
|
||||
},
|
||||
testRegion,
|
||||
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, tt.region)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(state, &tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *postgresflex.CreateDatabaseRequestPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
&Model{
|
||||
Name: types.StringValue("dbname"),
|
||||
Owner: types.StringValue("username"),
|
||||
},
|
||||
&postgresflex.CreateDatabaseRequestPayload{
|
||||
Name: utils.Ptr("dbname"),
|
||||
Owner: utils.Ptr("username"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"null_fields",
|
||||
&Model{
|
||||
Name: types.StringNull(),
|
||||
Owner: types.StringNull(),
|
||||
},
|
||||
&postgresflex.CreateDatabaseRequestPayload{
|
||||
Name: nil,
|
||||
Owner: nil,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.description, func(t *testing.T) {
|
||||
output, err := toCreatePayload(tt.input)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cleanString(t *testing.T) {
|
||||
type args struct {
|
||||
s *string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *string
|
||||
}{
|
||||
{
|
||||
name: "simple_value",
|
||||
args: args{
|
||||
s: utils.Ptr("mytest"),
|
||||
},
|
||||
want: utils.Ptr("mytest"),
|
||||
},
|
||||
{
|
||||
name: "simple_value_with_quotes",
|
||||
args: args{
|
||||
s: utils.Ptr("\"mytest\""),
|
||||
},
|
||||
want: utils.Ptr("mytest"),
|
||||
},
|
||||
{
|
||||
name: "simple_values_with_quotes",
|
||||
args: args{
|
||||
s: utils.Ptr("\"my test here\""),
|
||||
},
|
||||
want: utils.Ptr("my test here"),
|
||||
},
|
||||
{
|
||||
name: "simple_values",
|
||||
args: args{
|
||||
s: utils.Ptr("my test here"),
|
||||
},
|
||||
want: utils.Ptr("my test here"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := cleanString(tt.args.s); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("cleanString() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ package postgresflexalpha
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
|
|
@ -12,11 +14,23 @@ import (
|
|||
func DatabaseResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"database_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the database.",
|
||||
MarkdownDescription: "The ID of the database.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the database.",
|
||||
|
|
@ -28,12 +42,33 @@ func DatabaseResourceSchema(ctx context.Context) schema.Schema {
|
|||
Description: "The owner of the database.",
|
||||
MarkdownDescription: "The owner of the database.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
DatabaseId types.Int64 `tfsdk:"database_id"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue