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:
@@ -86,12 +86,35 @@ func TestWriteSubresource_VaultUserPassword(t *testing.T) {
|
||||
t.Errorf("password должен быть Computed + Sensitive, блок:\n%s", block)
|
||||
}
|
||||
|
||||
if !strings.Contains(src, "ExtractUserPassword(") || !strings.Contains(src, "GetInstanceVaultSecrets(") {
|
||||
t.Errorf("в Create нет чтения пароля из Vault родителя")
|
||||
if !strings.Contains(src, "ResolveUserPasswordFromVault(") {
|
||||
t.Errorf("нет чтения пароля из Vault родителя (ResolveUserPasswordFromVault)")
|
||||
}
|
||||
if !strings.Contains(src, "priorState.Password") {
|
||||
t.Errorf("в Update нет сохранения уже полученного пароля")
|
||||
}
|
||||
|
||||
// КРИТИЧНО: password — Computed-атрибут. Если хотя бы одна ветка выхода из
|
||||
// Create сохранит состояние, не проставив его, Terraform упадёт с
|
||||
// "Provider returned invalid result object after apply ... unknown value".
|
||||
// Поэтому: инициализация + вызов перед каждым сохранением состояния.
|
||||
if !strings.Contains(src, "plan.Password = types.StringNull()") {
|
||||
t.Errorf("password не инициализируется конкретным значением до ветвлений")
|
||||
}
|
||||
createBody := src
|
||||
if idx := strings.Index(src, ") Create("); idx > 0 {
|
||||
createBody = src[idx:]
|
||||
}
|
||||
if end := strings.Index(createBody, ") Read("); end > 0 {
|
||||
createBody = createBody[:end]
|
||||
}
|
||||
setCount := strings.Count(createBody, "resp.State.Set(ctx, &plan)")
|
||||
resolveCount := strings.Count(createBody, "ResolveUserPasswordFromVault(")
|
||||
if setCount == 0 {
|
||||
t.Fatalf("в Create не найдено сохранение состояния")
|
||||
}
|
||||
if resolveCount < setCount {
|
||||
t.Errorf("в Create %d сохранений состояния, но только %d вызовов заполнения пароля — в какой-то ветке password останется unknown", setCount, resolveCount)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("фича выключена: атрибута password нет", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user