fix(subresource): password заполняется во ВСЕХ ветках Create, а не только в успешной
Баг (воспроизведён на TEST 2026-10-01): при усыновлении уже существующего пользователя БД Create выходит по раннему return (ветка 'Подресурс уже существует' -> State.Set -> return), оставляя Computed-атрибут password в состоянии unknown. Terraform отказывался: 'Provider returned invalid result object after apply: the provider still indicated an unknown value for nubes_postgres_user.crud_user_0.password'. Исправление: - новая функция resources_core.ResolveUserPasswordFromVault(ctx, client, instanceUID, username, current): читает пароль из Vault родителя, иначе возвращает current, иначе типизированный null (null для Terraform — конкретное, known значение); - в шаблоне подресурса password инициализируется null сразу после получения instanceUID, а перед КАЖДЫМ resp.State.Set в Create проставляется конкретным значением — включая все ветки усыновления; - регрессионный тест усилен: считает сохранения состояния и вызовы заполнения в Create и падает, если хоть в одной ветке password останется unknown. Проверено: generated/test/go/90_postgres_user_resource.go — 4 State.Set и 4 вызова заполнения; go test ./... ok; go build — чисто.
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
package resources_core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"terraform-provider-nubes/internal/core"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
// ExtractUserPassword вытаскивает пароль пользователя из JSON-значения секрета Vault
|
||||
@@ -61,3 +66,39 @@ func ExtractUserPassword(secretJSON string, username string) (string, error) {
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ResolveUserPasswordFromVault читает пароль пользователя из секретов Vault
|
||||
// родительского инстанса и возвращает готовое значение для атрибута password.
|
||||
//
|
||||
// Зачем функция, а не код в шаблоне: подресурс сохраняет состояние в НЕСКОЛЬКИХ
|
||||
// местах (обычный create, усыновление уже существующего объекта, подтверждение по
|
||||
// state_out). Если хотя бы в одной ветке оставить password неизвестным, Terraform
|
||||
// падает с «Provider returned invalid result object after apply: ... still indicated
|
||||
// an unknown value». Поэтому все ветки обязаны проставлять КОНКРЕТНОЕ значение —
|
||||
// через эту функцию.
|
||||
//
|
||||
// Поведение:
|
||||
// - удалось прочитать пароль → конкретное значение;
|
||||
// - не удалось → возвращается current, если оно уже известно (например, из state),
|
||||
// иначе типизированный null (null тоже «known» для Terraform).
|
||||
func ResolveUserPasswordFromVault(ctx context.Context, client *core.UniversalClient, instanceUID string, username string, current types.String) types.String {
|
||||
if client != nil && strings.TrimSpace(instanceUID) != "" {
|
||||
if details, err := client.GetInstanceStateDetails(ctx, instanceUID); err == nil && details != nil && len(details.Vault.Fields) > 0 {
|
||||
if secrets, serr := client.GetInstanceVaultSecrets(ctx, instanceUID, details.Vault.Fields); serr == nil {
|
||||
for _, secretValue := range secrets {
|
||||
pw, perr := ExtractUserPassword(secretValue, username)
|
||||
if perr != nil {
|
||||
continue
|
||||
}
|
||||
if pw != "" {
|
||||
return types.StringValue(pw)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if current.IsNull() || current.IsUnknown() {
|
||||
return types.StringNull()
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user