gen(subresource): выход password для авто-генерируемого пароля пользователя
Проблема: у части сервисов пароль пользователя генерирует платформа и кладёт его в
секрет Vault РОДИТЕЛЬСКОГО инстанса. vault_secrets родителя — Computed и обновляется
только при его Read, поэтому внутри одного apply после create_user пароль недоступен
(Invalid index). Из-за этого в pg/outputs.tf приходилось читать vault_secrets кластера,
а стенду требовались два apply.
Решение: подресурс-пользователь отдаёт пароль СВОИМ выходом сразу после create_user.
- types.go: GenSubresource.VaultUserPassword (признак из данных спека);
- loader.go: признак = подресурс user + у сервиса есть vault-выходы + create_user
принимает username и НЕ принимает password; имя сервиса нигде не проверяется;
- templates/subresource.go: поле модели + Computed/Sensitive атрибут password,
чтение Vault родителя в Create (GetInstanceStateDetails + GetInstanceVaultSecrets)
и перенос уже полученного пароля в Update (чтобы Computed-атрибут не стал unknown);
- resources_core/subresource_user_password.go: ExtractUserPassword — разбор
{"<username>":{"password":"..."}} с безопасным возвратом пустой строки;
- writers: регрессионный тест «фича включена/выключена».
Проверено генерацией и сборкой test-стенда: выход получили 4 подресурса
(postgres_user, kafka_user, clickhouse_user, mongodb_user); mariadb_user НЕ затронут
(там пароль входной); k8s_*_user и vc_org_user не затронуты (пользователь
адресуется не через username). go test ./... — ok, go build — чисто.
This commit is contained in:
@@ -223,6 +223,23 @@ func LoadSpecs(dir string) ([]types.GenResource, []types.GenSubresource, []types
|
||||
params.AnalyzePlanModifiers(&sr.NeedsBoolPlanMod, &sr.NeedsInt64PlanMod, &sr.NeedsStringPlanMod, sr.SchemaParams)
|
||||
sr.NeedsJsonPlanMod = params.AnalyzeJsonPlanMod(sr.SchemaParams)
|
||||
sr.NeedsStringsImport = params.AnalyzeNeedsStrings(sr.ModifyParams)
|
||||
|
||||
// VaultUserPassword: пароль пользователя ГЕНЕРИРУЕТ платформа — в create-параметрах
|
||||
// подресурса нет кода password, — а сервис отдаёт vault-выходы. Значит пароль
|
||||
// появится в Vault родителя после create_user, и подресурс обязан вернуть его сам
|
||||
// (выход password), иначе потребителю придётся читать vault_secrets родителя, что
|
||||
// внутри одного apply невозможно. См. TOOLS/ARCHITECTURE.md.
|
||||
//
|
||||
// Признак берётся из ДАННЫХ спека, без сверки с именем сервиса: поэтому mariadb
|
||||
// (там пароль задаётся на входе create_user) сюда не попадает.
|
||||
//
|
||||
// Отдельно требуется параметр username: именно по нему провайдер находит пароль
|
||||
// в секрете Vault. У сервисов, где пользователь адресуется иначе (k8s_*_user,
|
||||
// vc_org_user), выход пока не генерируется.
|
||||
if sr.SubName == "user" && serviceHasVaultOutputs(spec) &&
|
||||
hasParamCode(sr.CreateParams, "username") && !hasParamCode(sr.CreateParams, "password") {
|
||||
sr.VaultUserPassword = true
|
||||
}
|
||||
subs = append(subs, *sr)
|
||||
}
|
||||
|
||||
@@ -460,8 +477,35 @@ func validateModifierOperation(i int, op *lib.OperationSpec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// analyzeUseStateForUnknown определяет, нужны ли импорты boolplanmodifier/int64planmodifier:
|
||||
// есть ли среди SchemaParams скалярный Optional+Computed (без Default) параметр
|
||||
// serviceHasVaultOutputs сообщает, отдаёт ли сервис секреты через Vault
|
||||
// (в outputs объявлены vault_secrets / vault_fields).
|
||||
//
|
||||
// Зачем: это признак «сервис держит секреты в Vault родителя». Используется вместе
|
||||
// с отсутствием входного password у create-подресурса, чтобы включить выход password
|
||||
// у подресурса-пользователя (см. VaultUserPassword в types.GenSubresource).
|
||||
// Признак берётся из данных спека — имя сервиса нигде не проверяется.
|
||||
func serviceHasVaultOutputs(spec types.ServiceSpec) bool {
|
||||
for _, p := range spec.Outputs.Params {
|
||||
switch strings.ToLower(strings.TrimSpace(p.Code)) {
|
||||
case "vault_secrets", "vault_fields":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasParamCode сообщает, есть ли среди параметров параметр с таким кодом
|
||||
// (регистр и пробелы не важны).
|
||||
func hasParamCode(params []types.Param, code string) bool {
|
||||
for _, p := range params {
|
||||
if strings.EqualFold(strings.TrimSpace(p.Code), code) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// analyzeUseStateForUnknown определяет, нужны ли импорты boolplanmodifier/int64planmodifier:// есть ли среди SchemaParams скалярный Optional+Computed (без Default) параметр
|
||||
// соответствующего типа, для которого генерируется UseStateForUnknown().
|
||||
func analyzeUseStateForUnknown(schemaParams []types.Param) (needsBool, needsInt64 bool) {
|
||||
for _, p := range schemaParams {
|
||||
|
||||
Reference in New Issue
Block a user