package resources_gen
import (
"context"
"strings"
"terraform-provider-nubes/internal/core"
"terraform-provider-nubes/internal/resources_core"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
)
// Code generated by tools/gen_v2. DO NOT EDIT.
// Service: postgres
// Subresource: user
var _ resource.Resource = &PostgresUserResource{}
// PostgresUserResource manages a subresource via operations.
type PostgresUserResource struct {
client *core.UniversalClient
}
type PostgresUserModel struct {
ID types.String `tfsdk:"id"`
PostgresID types.String `tfsdk:"postgres_id"`
AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"`
SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"`
OperationTimeout types.String `tfsdk:"operation_timeout"`
LogLevel types.String `tfsdk:"log_level"`
MtlsAccess types.Bool `tfsdk:"mtls_access"`
Role types.String `tfsdk:"role"`
Username types.String `tfsdk:"username"`
}
func NewPostgresUserResource() resource.Resource {
return &PostgresUserResource{}
}
func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_postgres_user"
}
func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
attrs := map[string]schema.Attribute{
"id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}},
"postgres_id": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
},
"adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)},
"skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)},
"operation_timeout": schema.StringAttribute{Optional: true},
"log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."},
"mtls_access": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Подключение пользователя без ввода паролей
Политика применяется **ко всем базам** внутри Postgresql
RBAC на базы настраивать вручную Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}},
"role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
"username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}},
}
resp.Schema = schema.Schema{Attributes: attrs}
}
func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan PostgresUserModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
if plan.Role.IsNull() || plan.Role.IsUnknown() {
resp.Diagnostics.AddError("Missing required attribute", "role is required.")
return
}
if plan.Username.IsNull() || plan.Username.IsUnknown() {
resp.Diagnostics.AddError("Missing required attribute", "username is required.")
return
}
instanceUID := strings.TrimSpace(plan.PostgresID.ValueString())
if instanceUID == "" {
resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра")
return
}
adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool()
idParams := map[string]string{
"mtlsAccess": resources_core.FormatBool(plan.MtlsAccess),
"role": resources_core.FormatString(plan.Role),
"username": resources_core.FormatString(plan.Username),
}
identityCodes := []string{
"mtlsAccess",
"role",
"username",
}
listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes)
targetValue := strings.TrimSpace(idParams[idKey])
if targetValue == "" {
for _, code := range identityCodes {
candidate := strings.TrimSpace(idParams[code])
if candidate == "" {
continue
}
targetValue = candidate
idKey = code
break
}
}
if targetValue != "" {
found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if known && found {
if adoptExistingOnCreate {
plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams))
resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue)
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
return
}
resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue)
return
}
}
params := resources_core.CompactParams(map[string]string{
"username": resources_core.FormatString(plan.Username),
"role": resources_core.FormatString(plan.Role),
"mtlsAccess": resources_core.FormatBool(plan.MtlsAccess),
})
operationTimeout := ""
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() {
operationTimeout = plan.OperationTimeout.ValueString()
}
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() {
ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString())
}
createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout)
if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) {
createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout)
}
if createErr != nil {
if targetValue != "" {
found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if checkErr != nil {
resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error())
return
}
if known && found {
plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams))
resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue)
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
return
}
}
if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) {
if targetValue != "" {
found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if checkErr != nil {
resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error())
return
}
if found || !known {
plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams))
resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state")
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
return
}
}
resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out")
return
}
resp.Diagnostics.AddError("Ошибка клиента", createErr.Error())
return
}
if targetValue != "" {
found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if known && !found {
resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue)
return
}
}
plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams))
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}
func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state PostgresUserModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan PostgresUserModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
if "" == "" {
var state PostgresUserModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
if plan.ID.IsNull() || plan.ID.IsUnknown() {
plan.ID = state.ID
}
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
return
}
instanceUID := strings.TrimSpace(plan.PostgresID.ValueString())
if instanceUID == "" {
resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра")
return
}
params := resources_core.CompactParams(map[string]string{})
idParams := map[string]string{
"mtlsAccess": resources_core.FormatBool(plan.MtlsAccess),
"role": resources_core.FormatString(plan.Role),
"username": resources_core.FormatString(plan.Username),
}
operationTimeout := ""
if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() {
operationTimeout = plan.OperationTimeout.ValueString()
}
if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() {
ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString())
}
if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams))
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}
func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state PostgresUserModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
if "delete_user" == "" {
return
}
instanceUID := strings.TrimSpace(state.PostgresID.ValueString())
if instanceUID == "" {
return
}
skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool()
idParams := map[string]string{
"mtlsAccess": resources_core.FormatBool(state.MtlsAccess),
"role": resources_core.FormatString(state.Role),
"username": resources_core.FormatString(state.Username),
}
identityCodes := []string{
"mtlsAccess",
"role",
"username",
}
listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes)
targetValue := strings.TrimSpace(idParams[idKey])
if targetValue == "" {
for _, code := range identityCodes {
candidate := strings.TrimSpace(idParams[code])
if candidate == "" {
continue
}
targetValue = candidate
idKey = code
break
}
}
if targetValue != "" {
found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if known && !found {
resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue)
return
}
}
params := resources_core.CompactParams(map[string]string{
"username": resources_core.FormatString(state.Username),
})
operationTimeout := ""
if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() {
operationTimeout = state.OperationTimeout.ValueString()
}
if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() {
ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString())
}
if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil {
if resources_core.IsSubresourceMissingError(err) {
if targetValue != "" {
found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if checkErr != nil {
resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error())
return
}
if known && found {
resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue)
return
}
}
if skipMissingOnDelete {
resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено")
return
}
}
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if targetValue != "" {
found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue)
if err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
}
if known && found {
resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue)
return
}
}
}
func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*core.UniversalClient)
if !ok {
resp.Diagnostics.AddError("Error", "Invalid client type")
return
}
r.client = client
}