## 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)
Reviewed-on: #57
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
717 lines
20 KiB
Go
717 lines
20 KiB
Go
package postgresflexalpha
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
|
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
|
|
postgresflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user/resources_gen"
|
|
postgresflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
|
postgresflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/postgresflexalpha"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
|
"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"
|
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
|
)
|
|
|
|
var (
|
|
// Ensure the implementation satisfies the expected interfaces.
|
|
_ resource.Resource = &userResource{}
|
|
_ resource.ResourceWithConfigure = &userResource{}
|
|
_ resource.ResourceWithImportState = &userResource{}
|
|
_ resource.ResourceWithModifyPlan = &userResource{}
|
|
_ resource.ResourceWithIdentity = &userResource{}
|
|
|
|
// Error message constants
|
|
extractErrorSummary = "extracting failed"
|
|
extractErrorMessage = "Extracting identity data: %v"
|
|
)
|
|
|
|
// NewUserResource is a helper function to simplify the provider implementation.
|
|
func NewUserResource() resource.Resource {
|
|
return &userResource{}
|
|
}
|
|
|
|
// resourceModel represents the Terraform resource state for a PostgreSQL Flex user.
|
|
type resourceModel = postgresflexalpha.UserModel
|
|
|
|
// UserResourceIdentityModel describes the resource's identity attributes.
|
|
type UserResourceIdentityModel struct {
|
|
ProjectID types.String `tfsdk:"project_id"`
|
|
Region types.String `tfsdk:"region"`
|
|
InstanceID types.String `tfsdk:"instance_id"`
|
|
UserID types.Int64 `tfsdk:"user_id"`
|
|
}
|
|
|
|
// userResource implements the resource handling for a PostgreSQL Flex user.
|
|
type userResource struct {
|
|
client *postgresflex.APIClient
|
|
providerData core.ProviderData
|
|
}
|
|
|
|
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
|
// Use the modifier to set the effective region in the current plan.
|
|
func (r *userResource) ModifyPlan(
|
|
ctx context.Context,
|
|
req resource.ModifyPlanRequest,
|
|
resp *resource.ModifyPlanResponse,
|
|
) { // nolint:gocritic // function signature required by Terraform
|
|
var configModel resourceModel
|
|
// skip initial empty configuration to avoid follow-up errors
|
|
if req.Config.Raw.IsNull() {
|
|
return
|
|
}
|
|
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
var planModel resourceModel
|
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Metadata returns the resource type name.
|
|
func (r *userResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
|
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_user"
|
|
}
|
|
|
|
// Configure adds the provider configured client to the resource.
|
|
func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
|
var ok bool
|
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
r.client = apiClient
|
|
tflog.Info(ctx, "Postgres Flex user client configured")
|
|
}
|
|
|
|
//go:embed planModifiers.yaml
|
|
var modifiersFileByte []byte
|
|
|
|
// Schema defines the schema for the resource.
|
|
func (r *userResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
|
s := postgresflexalpha.UserResourceSchema(ctx)
|
|
|
|
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
|
return
|
|
}
|
|
|
|
err = utils.AddPlanModifiersToResourceSchema(fields, &s)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
|
return
|
|
}
|
|
resp.Schema = s
|
|
}
|
|
|
|
// Create creates the resource and sets the initial Terraform state.
|
|
func (r *userResource) Create(
|
|
ctx context.Context,
|
|
req resource.CreateRequest,
|
|
resp *resource.CreateResponse,
|
|
) { // nolint:gocritic // function signature required by Terraform
|
|
var model resourceModel
|
|
diags := req.Plan.Get(ctx, &model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
arg := &clientArg{
|
|
projectId: model.ProjectId.ValueString(),
|
|
instanceId: model.InstanceId.ValueString(),
|
|
region: r.providerData.GetRegionWithOverride(model.Region),
|
|
}
|
|
|
|
ctx = r.setTFLogFields(ctx, arg)
|
|
|
|
var roles = r.expandRoles(ctx, model.Roles, &resp.Diagnostics)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Generate API request body from model
|
|
payload, err := toCreatePayload(&model, &roles)
|
|
if err != nil {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Creating API payload: %v", err))
|
|
return
|
|
}
|
|
|
|
// Create new user
|
|
userResp, err := r.client.CreateUserRequest(
|
|
ctx,
|
|
arg.projectId,
|
|
arg.region,
|
|
arg.instanceId,
|
|
).CreateUserRequestPayload(*payload).Execute()
|
|
if err != nil {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err))
|
|
return
|
|
}
|
|
|
|
id, ok := userResp.GetIdOk()
|
|
if !ok || id == 0 {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"Error creating user",
|
|
"API didn't return user Id. A user might have been created",
|
|
)
|
|
return
|
|
}
|
|
arg.userId = id
|
|
|
|
ctx = tflog.SetField(ctx, "user_id", id)
|
|
|
|
ctx = core.LogResponse(ctx)
|
|
|
|
// Set data returned by API in identity
|
|
identity := UserResourceIdentityModel{
|
|
ProjectID: types.StringValue(arg.projectId),
|
|
Region: types.StringValue(arg.region),
|
|
InstanceID: types.StringValue(arg.instanceId),
|
|
UserID: types.Int64Value(id),
|
|
}
|
|
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
model.Id = types.Int64Value(id)
|
|
model.UserId = types.Int64Value(id)
|
|
model.Password = types.StringValue(userResp.GetPassword())
|
|
model.Status = types.StringValue(userResp.GetStatus())
|
|
model.ConnectionString = types.StringValue(userResp.GetConnectionString())
|
|
|
|
waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler(
|
|
ctx,
|
|
r.client,
|
|
arg.projectId,
|
|
arg.instanceId,
|
|
arg.region,
|
|
id,
|
|
).SetSleepBeforeWait(
|
|
10 * time.Second,
|
|
).SetTimeout(
|
|
15 * time.Minute,
|
|
).WaitWithContext(ctx)
|
|
|
|
if err != nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"create user",
|
|
fmt.Sprintf("Instance creation waiting: %v", err),
|
|
)
|
|
return
|
|
}
|
|
|
|
if waitResp.Id == nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"create user",
|
|
"Instance creation waiting: returned id is nil",
|
|
)
|
|
return
|
|
}
|
|
if waitResp.Id == nil || *waitResp.Id != id {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"create user",
|
|
fmt.Sprintf(
|
|
"Instance creation waiting: returned id is wrong: %+v - %+v",
|
|
waitResp.Id,
|
|
id,
|
|
),
|
|
)
|
|
return
|
|
}
|
|
|
|
// Set state to fully populated data
|
|
diags = resp.State.Set(ctx, model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
tflog.Info(ctx, "Postgres Flex user created")
|
|
}
|
|
|
|
// Read refreshes the Terraform state with the latest data.
|
|
func (r *userResource) Read(
|
|
ctx context.Context,
|
|
req resource.ReadRequest,
|
|
resp *resource.ReadResponse,
|
|
) { // nolint:gocritic // function signature required by Terraform
|
|
var model resourceModel
|
|
diags := req.State.Get(ctx, &model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
arg := &clientArg{
|
|
projectId: model.ProjectId.ValueString(),
|
|
instanceId: model.InstanceId.ValueString(),
|
|
region: r.providerData.GetRegionWithOverride(model.Region),
|
|
}
|
|
|
|
ctx = r.setTFLogFields(ctx, arg)
|
|
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
// Read resource state
|
|
waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler(
|
|
ctx,
|
|
r.client,
|
|
arg.projectId,
|
|
arg.instanceId,
|
|
arg.region,
|
|
model.UserId.ValueInt64(),
|
|
).SetSleepBeforeWait(
|
|
10 * time.Second,
|
|
).SetTimeout(
|
|
15 * time.Minute,
|
|
).WaitWithContext(ctx)
|
|
|
|
if err != nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"read user",
|
|
fmt.Sprintf("Instance creation waiting: %v", err),
|
|
)
|
|
return
|
|
}
|
|
|
|
if waitResp.Id == nil || *waitResp.Id != model.UserId.ValueInt64() {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"read user",
|
|
"Instance creation waiting: returned id is nil or wrong",
|
|
)
|
|
return
|
|
}
|
|
arg.userId = *waitResp.Id
|
|
|
|
ctx = core.LogResponse(ctx)
|
|
|
|
// Set data returned by API in identity
|
|
identity := UserResourceIdentityModel{
|
|
ProjectID: types.StringValue(arg.projectId),
|
|
Region: types.StringValue(arg.region),
|
|
InstanceID: types.StringValue(arg.instanceId),
|
|
UserID: types.Int64Value(arg.userId),
|
|
}
|
|
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Set refreshed state
|
|
diags = resp.State.Set(ctx, model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
tflog.Info(ctx, "Postgres Flex user read")
|
|
}
|
|
|
|
// Update updates the resource and sets the updated Terraform state on success.
|
|
func (r *userResource) Update(
|
|
ctx context.Context,
|
|
req resource.UpdateRequest,
|
|
resp *resource.UpdateResponse,
|
|
) { // nolint:gocritic // function signature required by Terraform
|
|
var model resourceModel
|
|
diags := req.Plan.Get(ctx, &model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
arg := &clientArg{
|
|
projectId: model.ProjectId.ValueString(),
|
|
instanceId: model.InstanceId.ValueString(),
|
|
region: r.providerData.GetRegionWithOverride(model.Region),
|
|
}
|
|
|
|
ctx = r.setTFLogFields(ctx, arg)
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
// Retrieve values from state
|
|
var stateModel resourceModel
|
|
diags = req.State.Get(ctx, &stateModel)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
var roles = r.expandRoles(ctx, model.Roles, &resp.Diagnostics)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Generate API request body from model
|
|
payload, err := toUpdatePayload(&model, &roles)
|
|
if err != nil {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Updating API payload: %v", err))
|
|
return
|
|
}
|
|
|
|
userId64 := arg.userId
|
|
if userId64 > math.MaxInt32 {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (userId)")
|
|
return
|
|
}
|
|
userId := int32(userId64)
|
|
|
|
// Update existing instance
|
|
err = r.client.UpdateUserRequest(
|
|
ctx,
|
|
arg.projectId,
|
|
arg.region,
|
|
arg.instanceId,
|
|
userId,
|
|
).UpdateUserRequestPayload(*payload).Execute()
|
|
if err != nil {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", err.Error())
|
|
return
|
|
}
|
|
|
|
ctx = core.LogResponse(ctx)
|
|
|
|
// Set data returned by API in identity
|
|
identity := UserResourceIdentityModel{
|
|
ProjectID: types.StringValue(arg.projectId),
|
|
Region: types.StringValue(arg.region),
|
|
InstanceID: types.StringValue(arg.instanceId),
|
|
UserID: types.Int64Value(userId64),
|
|
}
|
|
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
// Verify update
|
|
waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler(
|
|
ctx,
|
|
r.client,
|
|
arg.projectId,
|
|
arg.instanceId,
|
|
arg.region,
|
|
model.UserId.ValueInt64(),
|
|
).SetSleepBeforeWait(
|
|
10 * time.Second,
|
|
).SetTimeout(
|
|
15 * time.Minute,
|
|
).WaitWithContext(ctx)
|
|
|
|
if err != nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"read user",
|
|
fmt.Sprintf("user update waiting: %v", err),
|
|
)
|
|
return
|
|
}
|
|
|
|
if waitResp.Id == nil || *waitResp.Id != model.UserId.ValueInt64() {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"update user",
|
|
"User creation waiting: returned id is nil or wrong",
|
|
)
|
|
return
|
|
}
|
|
arg.userId = *waitResp.Id
|
|
|
|
// Set state to fully populated data
|
|
diags = resp.State.Set(ctx, stateModel)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
tflog.Info(ctx, "Postgres Flex user updated")
|
|
}
|
|
|
|
// Delete deletes the resource and removes the Terraform state on success.
|
|
func (r *userResource) Delete(
|
|
ctx context.Context,
|
|
req resource.DeleteRequest,
|
|
resp *resource.DeleteResponse,
|
|
) { // nolint:gocritic // function signature required by Terraform
|
|
var model resourceModel
|
|
diags := req.State.Get(ctx, &model)
|
|
resp.Diagnostics.Append(diags...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
// Read identity data
|
|
var identityData UserResourceIdentityModel
|
|
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
arg, errExt := r.extractIdentityData(model, identityData)
|
|
if errExt != nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
extractErrorSummary,
|
|
fmt.Sprintf(extractErrorMessage, errExt),
|
|
)
|
|
}
|
|
|
|
ctx = r.setTFLogFields(ctx, arg)
|
|
ctx = core.InitProviderContext(ctx)
|
|
|
|
userId64 := arg.userId
|
|
if userId64 > math.MaxInt32 {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (userId)")
|
|
return
|
|
}
|
|
userId := int32(userId64)
|
|
|
|
// Delete existing record set
|
|
err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute()
|
|
if err != nil {
|
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
|
|
}
|
|
|
|
ctx = core.LogResponse(ctx)
|
|
|
|
// TODO: Verify deletion
|
|
//exists, err := r.getUserResource(ctx, &model, arg)
|
|
//if err != nil {
|
|
// core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
|
|
// return
|
|
//}
|
|
//if exists {
|
|
// core.LogAndAddError(
|
|
// ctx, &resp.Diagnostics, "Error deleting user",
|
|
// fmt.Sprintf("User ID '%v' resource still exists after deletion", model.UserId.ValueInt64()),
|
|
// )
|
|
// return
|
|
//}
|
|
|
|
resp.State.RemoveResource(ctx)
|
|
|
|
tflog.Info(ctx, "Postgres Flex user deleted")
|
|
}
|
|
|
|
// IdentitySchema defines the fields that are required to uniquely identify a resource.
|
|
func (r *userResource) IdentitySchema(
|
|
_ context.Context,
|
|
_ resource.IdentitySchemaRequest,
|
|
response *resource.IdentitySchemaResponse,
|
|
) {
|
|
response.IdentitySchema = identityschema.Schema{
|
|
Attributes: map[string]identityschema.Attribute{
|
|
"project_id": identityschema.StringAttribute{
|
|
RequiredForImport: true,
|
|
},
|
|
"region": identityschema.StringAttribute{
|
|
RequiredForImport: true,
|
|
},
|
|
"instance_id": identityschema.StringAttribute{
|
|
RequiredForImport: true,
|
|
},
|
|
"user_id": identityschema.Int64Attribute{
|
|
RequiredForImport: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// clientArg holds the arguments for API calls.
|
|
type clientArg struct {
|
|
projectId string
|
|
instanceId string
|
|
region string
|
|
userId int64
|
|
}
|
|
|
|
// ImportState imports a resource into the Terraform state on success.
|
|
// The expected import identifier format is: [project_id],[region],[instance_id],[database_id]
|
|
func (r *userResource) ImportState(
|
|
ctx context.Context,
|
|
req resource.ImportStateRequest,
|
|
resp *resource.ImportStateResponse,
|
|
) {
|
|
|
|
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 user",
|
|
fmt.Sprintf(
|
|
"Expected import identifier with format [project_id],[region],[instance_id],[user_id], got %q",
|
|
req.ID,
|
|
),
|
|
)
|
|
return
|
|
}
|
|
|
|
userId, err := strconv.ParseInt(idParts[3], 10, 64)
|
|
if err != nil {
|
|
core.LogAndAddError(
|
|
ctx,
|
|
&resp.Diagnostics,
|
|
"Error importing user",
|
|
fmt.Sprintf("Invalid user_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("user_id"), userId)...)
|
|
|
|
tflog.Info(ctx, "Postgres Flex user state imported")
|
|
|
|
return
|
|
}
|
|
|
|
// If no ID is provided, attempt to read identity attributes from the import configuration
|
|
var identityData UserResourceIdentityModel
|
|
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()
|
|
userId := identityData.UserID.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("user_id"), userId)...)
|
|
|
|
tflog.Info(ctx, "Postgres Flex user state imported")
|
|
}
|
|
|
|
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
|
|
func (r *userResource) extractIdentityData(
|
|
model resourceModel,
|
|
identity UserResourceIdentityModel,
|
|
) (*clientArg, error) {
|
|
|
|
var projectId, region, instanceId string
|
|
var userId int64
|
|
|
|
if !model.UserId.IsNull() && !model.UserId.IsUnknown() {
|
|
userId = model.UserId.ValueInt64()
|
|
} else {
|
|
if identity.UserID.IsNull() || identity.UserID.IsUnknown() {
|
|
return nil, fmt.Errorf("user_id not found in config")
|
|
}
|
|
userId = identity.UserID.ValueInt64()
|
|
}
|
|
|
|
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
|
projectId = model.ProjectId.ValueString()
|
|
} else {
|
|
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
|
|
return nil, fmt.Errorf("project_id not found in config")
|
|
}
|
|
projectId = identity.ProjectID.ValueString()
|
|
}
|
|
|
|
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
|
region = r.providerData.GetRegionWithOverride(model.Region)
|
|
} else {
|
|
if identity.Region.IsNull() || identity.Region.IsUnknown() {
|
|
return nil, 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 nil, fmt.Errorf("instance_id not found in config")
|
|
}
|
|
instanceId = identity.InstanceID.ValueString()
|
|
}
|
|
return &clientArg{
|
|
projectId: projectId,
|
|
instanceId: instanceId,
|
|
region: region,
|
|
userId: userId,
|
|
}, nil
|
|
}
|
|
|
|
// setTFLogFields adds relevant fields to the context for terraform logging purposes.
|
|
func (r *userResource) setTFLogFields(ctx context.Context, arg *clientArg) context.Context {
|
|
ctx = tflog.SetField(ctx, "project_id", arg.projectId)
|
|
ctx = tflog.SetField(ctx, "instance_id", arg.instanceId)
|
|
ctx = tflog.SetField(ctx, "region", arg.region)
|
|
ctx = tflog.SetField(ctx, "user_id", arg.userId)
|
|
|
|
return ctx
|
|
}
|
|
|
|
// expandRoles converts a Terraform list of roles to a string slice.
|
|
func (r *userResource) expandRoles(ctx context.Context, rolesSet types.List, diags *diag.Diagnostics) []string {
|
|
if rolesSet.IsNull() || rolesSet.IsUnknown() {
|
|
return nil
|
|
}
|
|
var roles []string
|
|
diags.Append(rolesSet.ElementsAs(ctx, &roles, false)...)
|
|
return roles
|
|
}
|