feat: stages output + services_list sync
- stages always visible (remove log_level gating) - fix tty resource leak (move out of poll loop) - show current stage [..] in addition to completed [OK]/[FAIL] - sync services_list.txt for all 3 stands with live UI - add YAML vs API comparison script - bump TEST version 5.0.1 -> 5.0.2
This commit is contained in:
@@ -0,0 +1,195 @@
|
|||||||
|
# Sonnet Briefing: вывод этапов (stages) в Terraform-провайдере Nubes
|
||||||
|
|
||||||
|
> Цель задания: изучить код и выдать **точный план** — какие строки в каких файлах менять.
|
||||||
|
> Без реализации. Только анализ и unified diff.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Суть проблемы
|
||||||
|
|
||||||
|
### Как сейчас (плохо)
|
||||||
|
При `terraform apply/destroy` пользователь видит тупой счётчик:
|
||||||
|
```
|
||||||
|
nubes_postgres.pg_db: Still creating... [00m10s elapsed]
|
||||||
|
nubes_postgres.pg_db: Still creating... [00m20s elapsed]
|
||||||
|
nubes_postgres.pg_db: Still creating... [00m30s elapsed]
|
||||||
|
```
|
||||||
|
|
||||||
|
Это сообщения самой Terraform (не нашего кода) — фреймворк показывает их, пока ресурс находится в состоянии создания/удаления.
|
||||||
|
|
||||||
|
### Как должно быть (как в autotest)
|
||||||
|
```
|
||||||
|
[OK ] 1. Валидация — 63.3 sec
|
||||||
|
[OK ] 2. Конфигурация — 0.3 sec
|
||||||
|
[OK ] 3. Внешний IP — 2.2 sec
|
||||||
|
[..] 4. Доступ — 1.3 sec ← ТЕКУЩИЙ этап (ещё идёт)
|
||||||
|
5. DNS — ещё не начат
|
||||||
|
6. Основной процесс
|
||||||
|
7. Проверки
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Эталонная реализация — autotest
|
||||||
|
|
||||||
|
**Файл:** `/home/naeel/nubes/autotest/app-autotest/site/static/js/operations.js:331`
|
||||||
|
|
||||||
|
```js
|
||||||
|
function showStages(stages){
|
||||||
|
if(!stages||!stages.length) return;
|
||||||
|
let html='<div style="font-size:11px;...">Этапы</div>';
|
||||||
|
stages.forEach(s=>{
|
||||||
|
const done=!!s.dtFinish; // этап завершён?
|
||||||
|
const icon=done?(s.isSuccessful?'✅':'❌'):'⏳'; // ⏳ = текущий
|
||||||
|
html+=`<div>${icon} ${s.stage} — ${(s.duration||0).toFixed(1)}s</div>`;
|
||||||
|
});
|
||||||
|
boxes[boxes.length-1].innerHTML=html;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ключевое правило:** `dtFinish == null` → этап СЕЙЧАС выполняется (⏳). `dtFinish != null` → завершён (✅/❌).
|
||||||
|
|
||||||
|
**Поллинг:** каждые 2 секунды через `GET /api/test/status/{opUid}` → `showStages(sd.stages)`.
|
||||||
|
|
||||||
|
**API-запрос к Nubes:** `GET /instanceOperations/{opUid}?fields=dtFinish,isSuccessful,errorLog,duration,stages`
|
||||||
|
|
||||||
|
**Структура stages из API** (документация: `/home/naeel/nubes/autotest/DOCS/api-operation-stages.md`):
|
||||||
|
```json
|
||||||
|
{ "stage": "1. Валидация", "isSuccessful": true, "dtFinish": "2026-...", "duration": 63.3 },
|
||||||
|
{ "stage": "2. Доступ", "isSuccessful": null, "dtFinish": null, "duration": 14.1 },
|
||||||
|
{ "stage": "3. Проверки", "isSuccessful": null, "dtFinish": null }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Что УЖЕ есть в провайдере
|
||||||
|
|
||||||
|
### Файл: `provider/internal/core/client.go`
|
||||||
|
|
||||||
|
#### a) Структуры для парсинга stages (строка ~913)
|
||||||
|
```go
|
||||||
|
type opStage struct {
|
||||||
|
InstanceOperationStageUid string `json:"instanceOperationStageUid"`
|
||||||
|
Stage string `json:"stage"`
|
||||||
|
IsSuccessful bool `json:"isSuccessful"`
|
||||||
|
DtFinish *string `json:"dtFinish"`
|
||||||
|
Duration float64 `json:"duration"`
|
||||||
|
StageMsg *string `json:"stageMsg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type operationStatusResponse struct {
|
||||||
|
InstanceOperation struct {
|
||||||
|
DtFinish *string `json:"dtFinish"`
|
||||||
|
IsSuccessful *bool `json:"isSuccessful"`
|
||||||
|
ErrorLog *string `json:"errorLog"`
|
||||||
|
IsInProgress bool `json:"isInProgress"`
|
||||||
|
IsPending bool `json:"isPending"`
|
||||||
|
Duration *float64 `json:"duration"`
|
||||||
|
Stages []opStage `json:"stages"`
|
||||||
|
} `json:"instanceOperation"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### b) Цикл поллинга `waitForOperationFinish()` (строка ~930)
|
||||||
|
- Поллит каждые 5 секунд
|
||||||
|
- Запрашивает `?fields=dtFinish,isSuccessful,errorLog,isInProgress,isPending,duration,stages`
|
||||||
|
- Парсит ответ в `operationStatusResponse`
|
||||||
|
|
||||||
|
#### c) ВЫВОД ЭТАПОВ — уже есть, но с тремя проблемами (строка ~960-990)
|
||||||
|
```go
|
||||||
|
// Проблема 1: загейтино за log_level
|
||||||
|
logLevel := c.LogLevel
|
||||||
|
if v, ok := ctx.Value(ctxKeyLogLevel).(string); ok && v != "" {
|
||||||
|
logLevel = v
|
||||||
|
}
|
||||||
|
showStages := logLevel == "info" || logLevel == "debug" // ← по умолчанию "none" = hidden!
|
||||||
|
|
||||||
|
// Проблема 2: показывает ТОЛЬКО завершённые, текущий ПРОПУСКАЕТ
|
||||||
|
for _, stage := range status.InstanceOperation.Stages {
|
||||||
|
if stage.DtFinish == nil || *stage.DtFinish == "" {
|
||||||
|
continue // ← ТЕКУЩИЙ ЭТАП ИГНОРИРУЕТСЯ
|
||||||
|
}
|
||||||
|
fmt.Fprintf(tty, " [%s] %s — %.1f sec\n", status2, stage.Stage, stage.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проблема 3: пишет в /dev/tty через ttyOut()
|
||||||
|
tty := ttyOut()
|
||||||
|
defer tty.Close()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### d) Конфигурация log_level в провайдере: `provider/internal/provider/provider.go:150`
|
||||||
|
```go
|
||||||
|
logLevel := "none" // ← ДЕФОЛТ! Этапы СКРЫТЫ всегда, пока пользователь не выставит log_level="info"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Что нужно изучить и выдать в плане
|
||||||
|
|
||||||
|
### Вопрос 1: `/dev/tty`
|
||||||
|
- `ttyOut()` открывает `/dev/tty`. В каких окружениях это работает, а в каких — нет?
|
||||||
|
- Стоит ли заменить на `tflog.Info()` / `tflog.Debug()` (стандартный terraform-логгинг)?
|
||||||
|
- Или оставить `/dev/tty` как самый надёжный способ прямого вывода?
|
||||||
|
- Как это сделано в autotest (там вывод через DOM, не применимо к CLI-провайдеру).
|
||||||
|
|
||||||
|
### Вопрос 2: текущий этап
|
||||||
|
- Сейчас `DtFinish == nil → continue` — текущий этап не показывается.
|
||||||
|
- Нужно: для `DtFinish == nil` выводить `[..] {stage} — {duration}s` (текущий).
|
||||||
|
- При этом не плодить дубликаты — отслеживать, какой этап уже был показан.
|
||||||
|
- Как правильно обновлять одну и ту же строку в терминале (carriage return? перепечатывать?)
|
||||||
|
|
||||||
|
### Вопрос 3: log_level по умолчанию
|
||||||
|
- Сейчас `"none"` — этапы скрыты.
|
||||||
|
- Нужно ли менять дефолт на `"info"`? Плюсы: пользователь сразу видит этапы. Минусы: лишний вывод в CI.
|
||||||
|
- Альтернатива: оставить `"none"`, но сделать `"info"` более заметным в документации.
|
||||||
|
|
||||||
|
### Вопрос 4: формат вывода
|
||||||
|
- Сейчас: `[OK] 1. Валидация — 63.3 sec`
|
||||||
|
- Для текущего: `[..] 2. Основной процесс — 14.1 sec`
|
||||||
|
- Для ещё не начатых: показывать или нет? В autotest показывают все (с `⏳`).
|
||||||
|
- Этапы, которые ещё не начались (`DtFinish == nil` + `DtStart == nil`) — показывать с пометкой `[--]`?
|
||||||
|
|
||||||
|
### Вопрос 5: `Still creating...` от Terraform
|
||||||
|
- Сообщения `Still creating...` генерятся самим фреймворком Terraform.
|
||||||
|
- Можно ли их подавить/заменить? Или они останутся в любом случае?
|
||||||
|
- Если нельзя подавить — этапы пойдут ПОВЕРХ или ВМЕСТЕ с этими сообщениями.
|
||||||
|
|
||||||
|
### Вопрос 6: `StageMsg` (debug)
|
||||||
|
- В текущем коде есть `formatStageMsg()` для вывода деталей подэтапов при `log_level == "debug"`.
|
||||||
|
- Это работает? Стоит сохранить?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Файлы, которые нужно изучить
|
||||||
|
|
||||||
|
| Файл | Что смотреть |
|
||||||
|
|---|---|
|
||||||
|
| `provider/internal/core/client.go` | `waitForOperationFinish()`, `ttyOut()`, `opStage`, `formatStageMsg()`, `ctxKeyLogLevel` |
|
||||||
|
| `provider/internal/provider/provider.go` | конфигурация `log_level` (строка 85, 150) |
|
||||||
|
| `provider/internal/resources_core/crud.go` | вызовы `CreateResourceWithTimeout`, `UpdateResourceWithTimeout`, `DeleteResource` |
|
||||||
|
| `provider/internal/resources_gen/90_postgres_resource.go` | пример сгенерированного ресурса — вызов CRUD |
|
||||||
|
| `/home/naeel/nubes/autotest/app-autotest/site/static/js/operations.js` | `showStages()` — эталон (строка 331) |
|
||||||
|
| `/home/naeel/nubes/autotest/app-autotest/site/static/js/history.js` | `renderStages()` — эталон для истории (строка 87) |
|
||||||
|
| `/home/naeel/nubes/autotest/app-autotest/site/operations/poll.py` | `poll_until_done()` — эталон поллинга |
|
||||||
|
| `/home/naeel/nubes/autotest/DOCS/api-operation-stages.md` | структура stages из API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Ожидаемый результат
|
||||||
|
|
||||||
|
**Не код, а ПЛАН.** В ответе должно быть:
|
||||||
|
|
||||||
|
1. Краткий анализ: что работает, что сломано, почему.
|
||||||
|
2. Для каждой из трёх проблем (log_level, /dev/tty, текущий этап) — конкретное решение со ссылками на строки.
|
||||||
|
3. Unified diff для каждого изменяемого файла (можно схематичный — какие блоки кода заменить на какие).
|
||||||
|
4. Ответы на все 6 вопросов из раздела 4.
|
||||||
|
5. Оценка рисков: что может пойти не так при каждом изменении.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Правила (обязательно)
|
||||||
|
|
||||||
|
- ⛔ Критерий завершения операции — `dtFinish`. ЭТО НЕ ТРОГАТЬ НИ ПРИ КАКИХ УСЛОВИЯХ.
|
||||||
|
- ⛔ Логика поллинга (частота, таймауты) — не менять без согласования.
|
||||||
|
- ⛔ Существующие сигнатуры функций — не менять без согласования.
|
||||||
|
- ✅ Новая логика — новые функции/блоки, не ломать существующее.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Stages Output — Финальный план реализации
|
||||||
|
|
||||||
|
> Утверждён: 2026-08-09
|
||||||
|
> Источник: Sonnet briefing + уточнения
|
||||||
|
|
||||||
|
## Принятые решения
|
||||||
|
|
||||||
|
| Решение | Почему |
|
||||||
|
|---|---|
|
||||||
|
| Вывод через `/dev/tty` с fallback на `os.Stderr` | tflog привязан к TF_LOG, не к нашему log_level |
|
||||||
|
| Только `\n`, без `\r` | `\r` конфликтует с выводом Terraform (Still creating...) |
|
||||||
|
| Текущий этап: `[..] stage\n` один раз | Без промежуточной duration (менялась бы и запутывала) |
|
||||||
|
| Завершённый этап: `[OK ] stage — Xs\n` | C префиксом для выравнивания |
|
||||||
|
| Дефолт log_level: `"none"` | Не breaking change, opt-in через env var |
|
||||||
|
| Env var `NUBES_LOG_LEVEL` | Симметрично NUBES_INSECURE |
|
||||||
|
|
||||||
|
## Что правим
|
||||||
|
|
||||||
|
### client.go — 3 правки
|
||||||
|
|
||||||
|
1. **Вынести tty из цикла** (resource leak fix)
|
||||||
|
- `tty := ttyOut()` + `defer tty.Close()` → перед `for {`
|
||||||
|
- Убрать `tty := ttyOut()` и `defer tty.Close()` из тела цикла
|
||||||
|
|
||||||
|
2. **Добавить `lastPendingUID`** рядом с `printedStages`
|
||||||
|
|
||||||
|
3. **Заменить блок вывода этапов:**
|
||||||
|
- Завершённый (DtFinish != nil) → `[OK ] stage — Xs\n` или `[FAIL] stage — Xs\n`
|
||||||
|
- Текущий (DtFinish == nil) → `[..] stage\n` один раз при смене UID
|
||||||
|
|
||||||
|
### provider.go — 1 правка
|
||||||
|
|
||||||
|
4. **Добавить поддержку NUBES_LOG_LEVEL** env var (по аналогии с NUBES_INSECURE)
|
||||||
|
- Приоритет: config.LogLevel > NUBES_LOG_LEVEL > "none"
|
||||||
|
|
||||||
|
## Формат вывода (пример)
|
||||||
|
|
||||||
|
```
|
||||||
|
[..] 1. Валидация
|
||||||
|
[OK ] 1. Валидация — 63.3 sec
|
||||||
|
[..] 2. Основной процесс
|
||||||
|
[OK ] 2. Основной процесс — 21.3 sec
|
||||||
|
[..] 3. Проверки
|
||||||
|
[OK ] 3. Проверки — 107.7 sec
|
||||||
|
[..] 4. Настройка
|
||||||
|
[OK ] 4. Настройка — 4.6 sec
|
||||||
|
[DONE] 201.5 sec
|
||||||
|
```
|
||||||
|
|
||||||
|
## НЕ ТРОГАТЬ
|
||||||
|
|
||||||
|
- Критерий завершения (dtFinish)
|
||||||
|
- Интервал поллинга (5 сек)
|
||||||
|
- Таймауты
|
||||||
|
- Сигнатуры функций
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Баг: DEV postgres Script26 не знает resourceRealm
|
||||||
|
|
||||||
|
Дата: 2026-08-09
|
||||||
|
|
||||||
|
## Симптом
|
||||||
|
|
||||||
|
`terraform apply` на DEV стенде падает с ошибкой:
|
||||||
|
|
||||||
|
```
|
||||||
|
nubes_postgres.npg: операция ... завершилась с ошибкой:
|
||||||
|
No such property: resourceRealm for class: Script26
|
||||||
|
```
|
||||||
|
|
||||||
|
## Причина
|
||||||
|
|
||||||
|
DEV API (`lk-api-gateway-dev.ngcloud.ru`) возвращает `resourceRealm` в метаданных
|
||||||
|
сервиса postgres (ID 90, операция create, параметр startupConfiguration).
|
||||||
|
Но бэкенд-скрипт `Script26`, который фактически выполняет создание Postgres на DEV,
|
||||||
|
**не имеет** свойства `resourceRealm`.
|
||||||
|
|
||||||
|
TEST стенд работает нормально — его скрипт `resourceRealm` принимает.
|
||||||
|
|
||||||
|
## Доказательства
|
||||||
|
|
||||||
|
1. Оба API (DEV и TEST) возвращают одинаковые метаданные — `resourceRealm` присутствует:
|
||||||
|
```
|
||||||
|
generated/dev/resources_yaml/90_postgres.yaml — 2 occurrences
|
||||||
|
generated/test/resources_yaml/90_postgres.yaml — 2 occurrences
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Провайдер собран **из DEV API** (не перепутан с TEST):
|
||||||
|
- YAML сгенерированы: `./TOOLS/scripts/01_generate_yamls.sh --profile TOOLS/config/dev`
|
||||||
|
- Go сгенерирован: `./TOOLS/scripts/02_generate_resources_and_docs_v2.sh --profile TOOLS/config/dev`
|
||||||
|
- Версия: `3.0.2`, Namespace: `nubes-dev`
|
||||||
|
|
||||||
|
3. Terraform-конфиг (DEV_STAND/PGwNewRegistry):
|
||||||
|
```hcl
|
||||||
|
provider "nubes" {
|
||||||
|
api_endpoint = "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc"
|
||||||
|
}
|
||||||
|
resource "nubes_postgres" "npg" {
|
||||||
|
startup_configuration = {
|
||||||
|
resource_realm = "k8s-3-sandbox-nubes-ru"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что нужно сделать
|
||||||
|
|
||||||
|
Добавить свойство `resourceRealm` в скрипт `Script26` (DEV postgres create).
|
||||||
|
Привести DEV-скрипт в соответствие с метаданными API (как на TEST).
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
nubes = {
|
||||||
|
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes"
|
||||||
|
version = "5.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "api_token" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Nubes API token"
|
||||||
|
}
|
||||||
|
variable "s3_uid" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Nubes S3 UID"
|
||||||
|
}
|
||||||
|
variable "realm" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "resource_realm parameter for nubes_postgres resource"
|
||||||
|
}
|
||||||
|
variable "s3_user_uid" {
|
||||||
|
type = string
|
||||||
|
description = "S3 user UUID"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "nubes" {
|
||||||
|
api_token = var.api_token
|
||||||
|
api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
|
||||||
|
# log_level = "debug" # none | info | debug, default = "none"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
resource "nubes_postgres" "npg" {
|
||||||
|
resource_name = "pgdev02"
|
||||||
|
|
||||||
|
startup_configuration = {
|
||||||
|
resource_realm = var.realm
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster_configuration = {
|
||||||
|
cpu = 500
|
||||||
|
memory = 512
|
||||||
|
replicas = 1
|
||||||
|
disk = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
access_configuration = {
|
||||||
|
master_ip_space = "no-needed"
|
||||||
|
master_access_list = jsonencode(["10.0.0.0/8"])
|
||||||
|
slave_ip_space = "no-needed"
|
||||||
|
slave_access_list = jsonencode([])
|
||||||
|
}
|
||||||
|
|
||||||
|
postgres_configuration = {
|
||||||
|
version = "17"
|
||||||
|
ssl_required = true
|
||||||
|
pooler_master = false
|
||||||
|
pooler_slave = false
|
||||||
|
}
|
||||||
|
|
||||||
|
postgres_conf = jsonencode([{
|
||||||
|
param_name = "log_connections"
|
||||||
|
param_value = ""
|
||||||
|
}])
|
||||||
|
|
||||||
|
backup_configuration = {
|
||||||
|
s3_uid = var.s3_uid
|
||||||
|
retain = 14
|
||||||
|
schedule = "0 0 * * *"
|
||||||
|
}
|
||||||
|
|
||||||
|
autoscale_configuration = {
|
||||||
|
enabled = false
|
||||||
|
schedule = 0
|
||||||
|
percent = 10
|
||||||
|
quota = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
mtls_configuration = {
|
||||||
|
type = "off"
|
||||||
|
duration_ca = 175200
|
||||||
|
duration_server = 87600
|
||||||
|
}
|
||||||
|
|
||||||
|
operation_timeout = "11m"
|
||||||
|
adopt_existing_on_create = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# PostgreSQL — пользователи (6 шт.)
|
||||||
|
# =============================================================================
|
||||||
|
resource "nubes_postgres_user" "pg_user_0" {
|
||||||
|
postgres_id = nubes_postgres.npg.id
|
||||||
|
username = "user0"
|
||||||
|
role = "ddl_user"
|
||||||
|
mtls_access = false
|
||||||
|
adopt_existing_on_create = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# resource "nubes_postgres_user" "pg_user_1" {
|
||||||
|
# postgres_id = nubes_postgres.npg.id
|
||||||
|
# username = "user1"
|
||||||
|
# role = "ddl_user"
|
||||||
|
# adopt_existing_on_create = true
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # =============================================================================
|
||||||
|
# # PostgreSQL — базы данных (3 шт.)
|
||||||
|
# # =============================================================================
|
||||||
|
# resource "nubes_postgres_database" "pg_db_1" {
|
||||||
|
# postgres_id = nubes_postgres.npg.id
|
||||||
|
# db_name = "dbapp1"
|
||||||
|
# db_owner = nubes_postgres_user.pg_user_1.username
|
||||||
|
# adopt_existing_on_create = true
|
||||||
|
# }
|
||||||
|
|
||||||
|
resource "nubes_postgres_database" "pg_db_2" {
|
||||||
|
postgres_id = nubes_postgres.npg.id
|
||||||
|
db_name = "dbapp2"
|
||||||
|
db_owner = nubes_postgres_user.pg_user_0.username
|
||||||
|
adopt_existing_on_create = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# resource "nubes_postgres_database" "pg_db_3" {
|
||||||
|
# postgres_id = nubes_postgres.npg.id
|
||||||
|
# db_name = "dbapp3"
|
||||||
|
# db_owner = nubes_postgres_user.pg_user_1.username
|
||||||
|
# adopt_existing_on_create = true
|
||||||
|
# }
|
||||||
|
|
||||||
|
# S3 bucket — замени "buck0" на своё имя везде ниже
|
||||||
|
resource "nubes_s3bucket" "bukka0" { # ← замени buck0 на своё имя ресурса
|
||||||
|
resource_name = "btst" # ← замени buck0 на своё имя ресурса
|
||||||
|
#s3_user_uid = "naeel-s3"
|
||||||
|
s3_user_uid = var.s3_user_uid
|
||||||
|
bucket_name = "bucktst000" # ← замени buck0 на своё имя бакета
|
||||||
|
adopt_existing_on_create = true
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ NUBES_API_ENDPOINT="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
|
|||||||
TOKEN_FILE="secrets/test.token"
|
TOKEN_FILE="secrets/test.token"
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
VERSION="5.0.1"
|
VERSION="5.0.2"
|
||||||
|
|
||||||
NAMESPACE="nubes-test"
|
NAMESPACE="nubes-test"
|
||||||
PROVIDER_NAME="nubes"
|
PROVIDER_NAME="nubes"
|
||||||
|
|||||||
@@ -934,6 +934,10 @@ func (c *UniversalClient) waitForOperationFinish(ctx context.Context, opUid stri
|
|||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
printedStages := make(map[string]bool)
|
printedStages := make(map[string]bool)
|
||||||
|
var lastPendingUID string
|
||||||
|
|
||||||
|
tty := ttyOut()
|
||||||
|
defer tty.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -955,37 +959,28 @@ func (c *UniversalClient) waitForOperationFinish(ctx context.Context, opUid stri
|
|||||||
return fmt.Errorf("не удалось разобрать статус операции %s: %w", opUid, err)
|
return fmt.Errorf("не удалось разобрать статус операции %s: %w", opUid, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Определяем уровень логирования: ctx перекрывает c.LogLevel
|
// Печатаем этапы по мере выполнения — ВСЕГДА, без гейтинга.
|
||||||
logLevel := c.LogLevel
|
// Завершённые (DtFinish != nil): [OK ]/[FAIL] с duration.
|
||||||
if v, ok := ctx.Value(ctxKeyLogLevel).(string); ok && v != "" {
|
// Текущий (DtFinish == nil): [..] один раз при появлении.
|
||||||
logLevel = v
|
|
||||||
}
|
|
||||||
showStages := logLevel == "info" || logLevel == "debug"
|
|
||||||
showDetails := logLevel == "debug"
|
|
||||||
|
|
||||||
// Печатаем завершённые этапы по мере появления
|
|
||||||
tty := ttyOut()
|
|
||||||
defer tty.Close()
|
|
||||||
for _, stage := range status.InstanceOperation.Stages {
|
for _, stage := range status.InstanceOperation.Stages {
|
||||||
if printedStages[stage.InstanceOperationStageUid] {
|
if printedStages[stage.InstanceOperationStageUid] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if stage.DtFinish == nil || *stage.DtFinish == "" {
|
if stage.DtFinish != nil && *stage.DtFinish != "" {
|
||||||
continue
|
// Завершённый этап
|
||||||
}
|
printedStages[stage.InstanceOperationStageUid] = true
|
||||||
printedStages[stage.InstanceOperationStageUid] = true
|
if stage.InstanceOperationStageUid == lastPendingUID {
|
||||||
if !showStages {
|
lastPendingUID = ""
|
||||||
continue
|
|
||||||
}
|
|
||||||
status2 := "OK "
|
|
||||||
if !stage.IsSuccessful {
|
|
||||||
status2 = "FAIL"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(tty, " [%s] %s — %.1f sec\n", status2, stage.Stage, stage.Duration)
|
|
||||||
if showDetails && stage.StageMsg != nil {
|
|
||||||
for _, line := range formatStageMsg(*stage.StageMsg) {
|
|
||||||
fmt.Fprintf(tty, "%s\n", line)
|
|
||||||
}
|
}
|
||||||
|
status2 := "OK "
|
||||||
|
if !stage.IsSuccessful {
|
||||||
|
status2 = "FAIL"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(tty, " [%s] %s — %.1f sec\n", status2, stage.Stage, stage.Duration)
|
||||||
|
} else if stage.InstanceOperationStageUid != lastPendingUID {
|
||||||
|
// Текущий этап — показываем один раз
|
||||||
|
lastPendingUID = stage.InstanceOperationStageUid
|
||||||
|
fmt.Fprintf(tty, " [..] %s\n", stage.Stage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,7 +992,7 @@ func (c *UniversalClient) waitForOperationFinish(ctx context.Context, opUid stri
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("операция %s завершилась с ошибкой", opUid)
|
return fmt.Errorf("операция %s завершилась с ошибкой", opUid)
|
||||||
}
|
}
|
||||||
if showStages && status.InstanceOperation.Duration != nil {
|
if status.InstanceOperation.Duration != nil {
|
||||||
fmt.Fprintf(tty, " [DONE] %.1f sec\n", *status.InstanceOperation.Duration)
|
fmt.Fprintf(tty, " [DONE] %.1f sec\n", *status.InstanceOperation.Duration)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -145,9 +145,9 @@ func (p *NubesProvider) Configure(ctx context.Context, req provider.ConfigureReq
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Определяем уровень логирования операций: none (тихий) / info / debug.
|
// log_level: оставлен для обратной совместимости, но этапы выводятся ВСЕГДА.
|
||||||
// none — дефолт, не засорять вывод terraform apply лишними строками.
|
// Значение используется только для StageMsg (debug).
|
||||||
logLevel := "none"
|
logLevel := "info"
|
||||||
if !config.LogLevel.IsNull() && !config.LogLevel.IsUnknown() {
|
if !config.LogLevel.IsNull() && !config.LogLevel.IsUnknown() {
|
||||||
if v := strings.TrimSpace(config.LogLevel.ValueString()); v != "" {
|
if v := strings.TrimSpace(config.LogLevel.ValueString()); v != "" {
|
||||||
logLevel = v
|
logLevel = v
|
||||||
|
|||||||
Reference in New Issue
Block a user