787 lines
45 KiB
Markdown
787 lines
45 KiB
Markdown
# Progress Log
|
||
|
||
## 2026-05-19 — PV Storage Migration + Fetcher v1.23.0
|
||
|
||
### Ветка: `feat/console-pv-storage` (git commit `2aadae3`, pushed)
|
||
|
||
**Задача:** Перевести storagesvc с S3 на PV (PersistentVolume), привести все образы к v1.23.0.
|
||
|
||
#### Что сделано
|
||
|
||
- `helm/fission-values.yaml` — `imageTag: v1.23.0`, `fetcher.image: naeel/fetcher`, `fetcher.imageTag: v1.23.0`, PV backend включён
|
||
- `naeel/fetcher:v1.23.0` — собран из `fission-src/cmd/fetcher/`, запушен в Docker Hub (отдельный бинарник, в `naeel/fission-bundle` не входит)
|
||
- `fission-storage-pvc` — Bound, 2Gi, `vcd-disk-ext4`, namespace `fission`; S3 полностью отключён
|
||
|
||
#### Баги найдены и задокументированы (`doc/report-2026-05-19-pv-migration-bugs.md`)
|
||
|
||
| # | Описание | Причина | Fix |
|
||
|---|----------|---------|-----|
|
||
| 1 | Helm upgrade откатил образы с v1.23.0 на v1.22.1 | `imageTag: v1.22.1` в values | Исправлено на v1.23.0 |
|
||
| 2 | Field manager конфликт при helm upgrade | fission-console патчит FISSION_RESOURCE_NAMESPACES | Перед upgrade: `kubectl delete deploy executor router -n fission` |
|
||
| 3 | Pool pods ImagePullBackOff — `FETCHER_IMAGE=fission/fetcher:v1.22.0` (не существует) | fetcher — отдельный образ, его нет в fission-bundle | Собрали `naeel/fetcher:v1.23.0` |
|
||
| 4 | Router JWT password сменился после helm upgrade | Новый secret | Console читает динамически — работает; CLI токены устарели |
|
||
|
||
#### Тест-прогон `run_all.sh` — PASS=8 FAIL=0 (319 сек)
|
||
|
||
| Скрипт | Результат | Кол-во проверок |
|
||
|--------|-----------|----------------|
|
||
| test_auth.sh | ✅ PASS | 14 |
|
||
| test_functions_crud.sh | ✅ PASS | 29 |
|
||
| test_ttl.sh | ✅ PASS | 10 |
|
||
| test_isolation.sh | ✅ PASS | 11 |
|
||
| test_update.sh | ✅ PASS | 10 |
|
||
| test_invoke.sh | ✅ PASS | 9 |
|
||
| test_linters.sh | ✅ PASS | 31 |
|
||
| test_stress.sh | ✅ PASS | 3 (8 параллельных пользователей) |
|
||
|
||
Лог: `test-results/2026-05-19_08-51-31.log`
|
||
|
||
---
|
||
|
||
## TODO (backlog)
|
||
|
||
### Биллинг / статистика для бухгалтерии
|
||
Цель: считать потреблённые ресурсы по пользователям для тарификации.
|
||
- Метрики: количество вызовов, время выполнения (ms), CPU/RAM пода
|
||
- Агрегация по namespace (пользователю) за период (день/месяц)
|
||
- Хранение истории (сейчас metrics-collector хранит только текущее)
|
||
- Отчёт: пользователь → вызовы + суммарное время → сумма к оплате
|
||
- API для бухгалтерии или выгрузка CSV/Excel
|
||
- Что уже есть: metrics-collector (/metrics endpoint), Fission router логирует вызовы, k8s metrics-server
|
||
- Примерная модель: X руб/1000 вызовов + Y руб/GB·сек
|
||
|
||
### MQ-триггер и Kube-Watch триггер в консоли
|
||
- Backend: добавить GVR для messagequeuetriggers.fission.io и kuberneteswatchtriggers.fission.io
|
||
- UI: формы создания по аналогии с cron, счётчики на главной
|
||
|
||
### Прочий полезный функционал
|
||
- ~~Клонировать функцию (копия с новым именем)~~ ✅ v1.3.80
|
||
- ~~Переменные окружения (env vars) через UI~~ ✅ v1.3.78
|
||
- Метрики на главной странице (данные уже есть в metrics-collector)
|
||
- Автообновление логов (polling каждые 3-5 сек)
|
||
- Копировать маршрут одной кнопкой (📋 рядом с маршрутом)
|
||
|
||
---
|
||
|
||
## 2026-05-09 — 2026-05-10
|
||
|
||
### Ветка: `feat/function-status-clone-envvars` / `feat/clone-function`
|
||
|
||
---
|
||
|
||
#### v1.3.78 — Переменные окружения: патч Deployment напрямую
|
||
|
||
**Проблема:** Fission newdeploy executor игнорирует `fn.Spec.PodSpec` при построении
|
||
Deployment — применяет только `env.Spec.Runtime.Container`. Сохранение env vars в
|
||
Function CRD через UI не приводило к попаданию переменных в pod.
|
||
|
||
**Решение:** После обновления Function CRD — дополнительно патчить Deployment
|
||
напрямую через Kubernetes API (`StrategicMergePatch`).
|
||
|
||
- Файл: `console/internal/api/function_crud.go`
|
||
- Новая функция `patchDeploymentEnvVars(ctx, ns, fnName, envContainerName, envVars)`
|
||
- Поиск Deployment по labels: `functionName=<name>,functionNamespace=<ns>,executorType=newdeploy`
|
||
- Patch: `StrategicMergePatch` с `containers[{name: envContainerName, env: [...]}]`
|
||
- Импорт добавлен: `k8stypes "k8s.io/apimachinery/pkg/types"`
|
||
- Образ: `naeel/fission-console:v1.3.78`
|
||
|
||
---
|
||
|
||
#### v1.3.79 — LLM: убрано ограничение тем чата + сброс кэша роутера
|
||
|
||
**Проблема 1:** LLM ассистент отказывался отвечать на вопросы не по теме Fission Console.
|
||
|
||
**Решение:** Удалён блок `## Чем НЕ занимаешься` из `buildChatSystemPrompt()` в
|
||
`console/internal/api/ai_prompts.go`.
|
||
|
||
**Проблема 2:** Env vars всё ещё не работали после v1.3.78 — роутер закешировал
|
||
старый poolmgr адрес (funcSvc cache).
|
||
|
||
**Решение:** `kubectl rollout restart deployment/router -n fission` — сбросил кэш.
|
||
После перезапуска env vars начали попадать в поды.
|
||
|
||
- Образ: `naeel/fission-console:v1.3.79`
|
||
|
||
---
|
||
|
||
#### v1.3.80 — Клонирование функции (полная реализация)
|
||
|
||
**Фича:** `POST /functions/:name/clone` — создаёт полную копию функции с новым именем.
|
||
|
||
**Backend** (`console/internal/api/function_clone.go`, новый файл):
|
||
1. Валидирует `new_name` (regex `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`, ≤57 символов)
|
||
2. Скачивает архив из storagesvc (type:url) или base64 (type:literal)
|
||
3. Заливает как новый архив в storagesvc (новый S3-объект — удаление оригинала не ломает клон)
|
||
4. Создаёт новый Package с именем `<new_name>-<timestamp>`
|
||
5. Копирует аннотации: `language`, `source-type`, `env-vars`, `archive-filename`, добавляет `cloned-from`
|
||
6. Копирует entrypoint, timeout, InvokeStrategy, podspec
|
||
7. Создаёт Function CRD
|
||
8. Копирует HTTP методы из оригинального триггера, создаёт HTTPTrigger
|
||
9. При ошибке Function/Trigger — откатывает созданные Package/Function
|
||
|
||
**Route** (`console/internal/api/handlers.go`):
|
||
```
|
||
POST /functions/:name/clone
|
||
```
|
||
|
||
**Frontend** (`console/ui/index.html`, `console/ui/js/functions.js`):
|
||
- Кнопка **📋 Клон** в нижней панели edit modal
|
||
- Inline форма: поле имени, кнопки «Клонировать» / «✕»
|
||
- Предзаполнение именем `<original>-copy`
|
||
- Валидация на клиенте (тот же regex)
|
||
- При успехе: закрытие модалки + polling до появления в таблице
|
||
|
||
**Тест результат:**
|
||
```
|
||
POST /console/api/functions/clone-src/clone {"new_name":"clone-src-copy"}
|
||
→ 201 {"cloned_from":"clone-src","name":"clone-src-copy","package":"clone-src-copy-20260509162619","route":"/47b6ef5e8225/clone-src-copy"}
|
||
kubectl get function clone-src-copy ✅
|
||
kubectl get package clone-src-copy-... ✅ type:url storagesvc
|
||
kubectl get httptrigger clone-src-copy-route ✅ methods=["GET"]
|
||
annotation: fission-console/cloned-from=clone-src ✅
|
||
```
|
||
|
||
- Образ: `naeel/fission-console:v1.3.80`
|
||
|
||
---
|
||
|
||
#### v1.3.81 — Fix: clone не обновлял таблицу (`loadFunctions` → `reloadAll`)
|
||
|
||
**Проблема:** После клонирования таблица не обновлялась — функция `loadFunctions()`
|
||
не существует, правильное название `reloadAll()`.
|
||
|
||
**Fix:** `console/ui/js/functions.js` — замена вызова.
|
||
|
||
- Образ: `naeel/fission-console:v1.3.81`
|
||
|
||
---
|
||
|
||
#### v1.3.82 — Fix: `apiFetch is not defined` → `requestJSON`
|
||
|
||
**Проблема:** `apiFetch` не существует в codebase. Правильный хелпер — `requestJSON(url, method, body)` из `api.js`.
|
||
|
||
**Fix:** `console/ui/js/functions.js` — переписан `submitClone()` на `requestJSON`.
|
||
|
||
- Образ: `naeel/fission-console:v1.3.82`
|
||
|
||
---
|
||
|
||
#### v1.3.83 — Fix: clone — задержка 800мс перед reloadAll (race condition)
|
||
|
||
**Проблема:** После клонирования таблица обновлялась раньше, чем k8s возвращал
|
||
новый объект в List-ответе.
|
||
|
||
**Fix:** `setTimeout(reloadAll, 800)` → заменено на polling с ожиданием появления
|
||
функции в `S.fns` (до 5 попыток × 1 сек).
|
||
|
||
- Образ: `naeel/fission-console:v1.3.83`
|
||
|
||
---
|
||
|
||
#### v1.3.84 — Help + LLM промпты: разделы env vars и clone
|
||
|
||
**Help modal** (`console/ui/index.html`):
|
||
- Новая секция **«Переменные окружения функции»**: как добавлять через UI, как читать в коде (`os.environ` / `process.env` / `os.Getenv`)
|
||
- Новая секция **«Клонирование функции»**: кнопка 📋 Клон, что копируется, правила имени
|
||
|
||
**LLM системный промпт** (`console/internal/api/ai_prompts.go`):
|
||
- В раздел «Редактирование функции» добавлены пункты «Переменные окружения» и «📋 Клон»
|
||
|
||
- Образ: `naeel/fission-console:v1.3.84`
|
||
|
||
---
|
||
|
||
#### v1.3.85 — Fix: polling до появления клона в таблице
|
||
|
||
**Проблема:** После клонирования — резкий переход в таблицу, клон ещё не виден
|
||
(k8s list не успел вернуть новый объект).
|
||
|
||
**Fix:** `console/ui/js/functions.js` — polling loop:
|
||
1. Закрыть модалку
|
||
2. Показать статус «Клонирование завершено, обновляем список...»
|
||
3. Через 700мс — `reloadAll()`
|
||
4. Проверить `S.fns` на наличие нового имени
|
||
5. Если не найден — повторить через 1 сек (до 5 попыток)
|
||
6. Показать финальное сообщение
|
||
|
||
- Образ: `naeel/fission-console:v1.3.85`
|
||
|
||
---
|
||
|
||
#### Unit-тесты (`console/internal/api/function_clone_test.go`)
|
||
|
||
21 тест, все PASS. Файл создан с нуля.
|
||
|
||
| Группа | Тесты |
|
||
|---|---|
|
||
| Валидация имени | пустое → 400; uppercase → 400; начинается с `-` → 400; заканчивается `-` → 400; 58 символов → 400; 57 символов → OK (404); имя с дефисами → OK |
|
||
| Источник не найден | 404 с именем функции в теле |
|
||
| Успешный клон (type:literal) | Package создан; Function создана; аннотации `cloned-from`, `language`, `source-type` скопированы; entrypoint и timeout скопированы; HTTPTrigger с методами и маршрутом |
|
||
| Маршрут | явный `/custom/path`; автодобавление `/` при отсутствии |
|
||
| Конфликт имени | дублирующее → 409 |
|
||
| Откат | ошибка создания Function → Package удалён |
|
||
| downloadPackageBytes | type:literal; type:url; пустой Package → ошибка |
|
||
| downloadFromStoragesvc | HTTP 404 → ошибка с кодом |
|
||
| getTriggerMethods | триггер найден; триггер не найден |
|
||
| Невалидный JSON | → 400 |
|
||
| Ссылка Package→Function | `spec.package.packageref.name` совпадает с реальным Package |
|
||
|
||
```
|
||
ok fission-console/internal/api 0.040s
|
||
```
|
||
|
||
---
|
||
|
||
|
||
|
||
### План перед работой
|
||
- Зафиксировать, что REST `/v2/*` недоступен в текущем кластере Fission v1.22.1 и MVP идет через Kubernetes CRD API.
|
||
- Реализовать первый рабочий Terraform ресурс `fission_environment`.
|
||
- Добавить минимальный CRUD в клиент для CRD `environments.fission.io`.
|
||
- Проверить сборку `go build ./...`.
|
||
|
||
### Факт выполнения
|
||
- Обновлен план в `doc/CODEX_PLAN.md`: путь реализации переведен на CRD API.
|
||
- Создан каркас провайдера и заготовки ресурсов.
|
||
- Реализован рабочий `fission_environment`:
|
||
- schema (name, image, version, poolsize, namespace, id, uid)
|
||
- Configure() с получением клиента из provider
|
||
- Create/Read/Update/Delete через Kubernetes CRD `environments.fission.io`
|
||
- В `internal/client/client.go` добавлены методы:
|
||
- CreateEnvironment / GetEnvironment / UpdateEnvironment / DeleteEnvironment
|
||
- IsNotFound для обработки удаления/дрейфа state
|
||
- Проверка `go build ./...` проходит успешно.
|
||
|
||
### Следующий шаг
|
||
- Реализовать `fission_package` (zip/base64 -> spec.source.literal) через CRD `packages.fission.io`.
|
||
|
||
### Обновление этапа (fission_package)
|
||
- Реализован CRUD в `internal/client/client.go` для `packages.fission.io`:
|
||
- `CreatePackage`
|
||
- `GetPackage`
|
||
- `UpdatePackage`
|
||
- `DeletePackage`
|
||
- Реализован ресурс `fission_package` в `internal/resources/package_resource.go`:
|
||
- schema: `name`, `environment`, `source_dir`, `code_path`, `code_hash`, `build_command`, `namespace`, computed `id`, `uid`, `build_status`, `build_log`
|
||
- валидация источника кода: задан ровно один из `source_dir` или `code_path`
|
||
- упаковка `source_dir` в zip и кодирование base64 в `spec.source.literal`
|
||
- Create/Read/Update/Delete через Kubernetes CRD API
|
||
- Проверка сборки: `go build ./...` успешно.
|
||
|
||
### Следующий шаг
|
||
- Реализовать `fission_function` через CRD `functions.fission.io`.
|
||
|
||
### Обновление этапа (fission_function)
|
||
- Реализован CRUD в `internal/client/client.go` для `functions.fission.io`:
|
||
- `CreateFunction`
|
||
- `GetFunction`
|
||
- `UpdateFunction`
|
||
- `DeleteFunction`
|
||
- Реализован ресурс `fission_function` в `internal/resources/function_resource.go`:
|
||
- schema: `name`, `environment`, `package_name`, `entrypoint`, `namespace`, computed `id`, `uid`
|
||
- Configure/Create/Read/Update/Delete
|
||
- маппинг в CRD `Function` (`spec.environment`, `spec.package.packageref`, `spec.package.functionName`)
|
||
- Проверка сборки: `go build ./...` успешно.
|
||
|
||
### Следующий шаг
|
||
- Реализовать `fission_http_trigger` через CRD `httptriggers.fission.io`.
|
||
|
||
### Обновление этапа (fission_http_trigger)
|
||
- Реализован CRUD в `internal/client/client.go` для `httptriggers.fission.io`:
|
||
- `CreateHTTPTrigger`
|
||
- `GetHTTPTrigger`
|
||
- `UpdateHTTPTrigger`
|
||
- `DeleteHTTPTrigger`
|
||
- Реализован ресурс `fission_http_trigger` в `internal/resources/http_trigger_resource.go`:
|
||
- schema: `name`, `function`, `url`, `methods`, `create_ingress`, `host`, `namespace`, computed `id`, `uid`
|
||
- Configure/Create/Read/Update/Delete
|
||
- маппинг в CRD `HTTPTrigger` (`spec.relativeurl`, `spec.methods`, `spec.functionref`, `spec.createingress`)
|
||
- Проверка сборки: `go build ./...` успешно.
|
||
|
||
### Следующий шаг
|
||
- Добавить пример `examples/hello-python/main.tf` и выполнить smoke Terraform apply.
|
||
|
||
### Обновление этапа (пример Terraform)
|
||
- Создан пример `examples/hello-python/main.tf` с использованием ресурсов:
|
||
- `fission_environment`
|
||
- `fission_package`
|
||
- `fission_function`
|
||
- `fission_http_trigger`
|
||
- Добавлен код функции `examples/hello-python/code/main.py`.
|
||
|
||
### Следующий шаг
|
||
- Подготовить локальную установку бинарника провайдера для `terraform init` и выполнить smoke `terraform apply`.
|
||
|
||
### Обновление этапа (smoke Terraform apply)
|
||
- Собран бинарник провайдера и подключен через Terraform `dev_overrides`.
|
||
- Прогнан `terraform plan` для `examples/hello-python`: план успешен, 4 ресурса к созданию.
|
||
- Прогнан `terraform apply` и выполнена функциональная проверка.
|
||
|
||
Исправленные баги по факту smoke:
|
||
- `fission_package`: после Create computed-поля (`build_status`, `build_log`) могли оставаться `unknown`.
|
||
- Исправлено формирование state в `unstructuredToPackageModel`.
|
||
- `fission_function`: CRD требовал обязательный `spec.InvokeStrategy`.
|
||
- Добавлен `InvokeStrategy` в payload функции.
|
||
- `fission_http_trigger`: несогласованность state (`create_ingress` null -> false).
|
||
- В schema добавлены `Computed` + `Default(false)` для `create_ingress`.
|
||
- Для `methods` добавлен `Computed`.
|
||
|
||
Дополнительно:
|
||
- Для текущего python MVP `fission_package` переведен на `spec.deployment.literal` (как у рабочего CLI-пакета).
|
||
- Проверка endpoint после apply:
|
||
- `GET /tf-hello` -> `hello from fission via terraform`.
|
||
|
||
### Следующий шаг
|
||
- Добавить README по запуску dev override и оговорить ограничения текущего MVP (`source_dir` ожидает `main.py`).
|
||
|
||
### Обновление этапа (README)
|
||
- Добавлен `README.md` в корень репозитория.
|
||
- Описаны:
|
||
- список реализованных ресурсов MVP
|
||
- запуск через Terraform dev override
|
||
- текущие ограничения MVP
|
||
|
||
### Обновление этапа (import + тесты)
|
||
- Добавлена поддержка `terraform import` для всех 4 ресурсов:
|
||
- `fission_environment`
|
||
- `fission_package`
|
||
- `fission_function`
|
||
- `fission_http_trigger`
|
||
- Формат import ID: `namespace/name`.
|
||
- Добавлены unit-тесты в `terraform/provider/internal/resources/*_test.go`:
|
||
- парсинг import ID
|
||
- конвертация model <-> unstructured
|
||
- валидация package source
|
||
- базовая проверка HTTP trigger round-trip
|
||
- Прогон валидации:
|
||
- `go test ./...` — успешно
|
||
- smoke `terraform plan/apply` — успешно
|
||
- endpoint `GET /tf-hello` — `hello from fission via terraform`
|
||
|
||
### Следующий шаг
|
||
- Добавить acceptance-тесты с опциональным запуском по env-флагу.
|
||
|
||
### Обновление этапа (acceptance тест)
|
||
- Добавлен acceptance-тест `terraform/provider/internal/client/client_acceptance_test.go`.
|
||
- Тест выполняет живой CRUD в кластере для цепочки:
|
||
- Environment -> Package -> Function -> HTTPTrigger
|
||
- Запуск теста:
|
||
- `ACCEPTANCE_TEST=1 KUBECONFIG=/home/naeel/.kube/config go test ./internal/client -run TestAcceptanceFissionCRUD -v`
|
||
- Результат: PASS.
|
||
|
||
### Следующий шаг
|
||
- Расширить acceptance-тест проверкой реального HTTP-вызова функции через ingress.
|
||
|
||
### Обновление этапа (много функций + суровая нагрузка)
|
||
- Добавлен `examples/stress-suite`:
|
||
- функции: `fast`, `cpu`, `json`
|
||
- маршруты: `/stress/fast`, `/stress/cpu`, `/stress/json`
|
||
- Terraform-конфиг на 10 ресурсов (env + 3 package + 3 function + 3 trigger)
|
||
- Добавлен скрипт `examples/stress-suite/scripts/load_test.sh` для параллельной нагрузки.
|
||
- Реальный прогон после донастройки:
|
||
- `900` запросов на endpoint
|
||
- `90` параллелизм
|
||
- итог: `100%` success на всех трех endpoint-ах.
|
||
|
||
### Следующий шаг
|
||
- Добавить в acceptance-тест проверку HTTP latency/perf budget и порогов SLO.
|
||
|
||
### Обновление этапа (JS/Go source support)
|
||
- В `fission_package` расширена поддержка `source_dir`:
|
||
- `main.py`
|
||
- `main.js`
|
||
- `main.go`
|
||
- Добавлены unit-тесты на выбор JS/Go исходников в `package_resource_test.go`.
|
||
- Прогон `go test ./...` — успешно.
|
||
|
||
### Следующий шаг
|
||
- Добавить отдельные Terraform примеры для Node.js и Go функций.
|
||
|
||
---
|
||
|
||
## 2026-04-15
|
||
|
||
### Fission Console — Web UI для управления функциями
|
||
|
||
**Ветка:** `feat/console`
|
||
|
||
#### Реализовано (14 апреля, вечер → 15 апреля, утро)
|
||
|
||
**Backend (console/main.go):**
|
||
- Go HTTP-сервер с embedded UI
|
||
- CRUD API для Fission CRD через dynamic k8s client:
|
||
- `POST /api/functions` — создание (Package + Function + HTTPTrigger атомарно с rollback)
|
||
- `GET /api/functions` — список всех
|
||
- `GET /api/functions/{name}` — детали (с декодированным кодом, route, methods)
|
||
- `PUT /api/functions/{name}/code` — обновление кода
|
||
- `POST /api/functions/{name}/invoke` — вызов функции через роутер
|
||
- `DELETE /api/functions/{name}` — удаление (trigger + function + package)
|
||
- Двойная маршрутизация: `/api/*` и `/console/api/*` (для работы через ingress `/console/`)
|
||
- CORS middleware, request logging
|
||
|
||
**UI (console/ui/index.html):**
|
||
- SPA-дашборд с карточками (environments, packages, functions, httptriggers, timetriggers)
|
||
- Таблица функций с кнопками Edit/Invoke/Delete
|
||
- Модал Create: выбор environment, ввод кода, route, methods
|
||
- Модал Edit Code: редактирование и сохранение
|
||
- Модал Invoke: отправка запроса, отображение статуса/latency/response
|
||
- Модал Delete: подтверждение
|
||
- Динамический `API_BASE` для работы как с `/` так и с `/console/` prefix
|
||
|
||
**Тесты (console/main_test.go):**
|
||
- 5 unit-тестов:
|
||
- `TestNormalizeMethods` — нормализация HTTP-методов
|
||
- `TestCreateFunctionValidation` — валидация обязательных полей
|
||
- `TestCreateFunctionSuccessAndGetDetails` — полный CRUD flow + Get с декодированным кодом
|
||
- `TestUpdateFunctionCode` — обновление кода в package
|
||
- `TestInvokeFunctionWithJWTAuth` — invoke с mock-роутером, проверка JWT auth flow
|
||
- Используется `dynamicfake.NewSimpleDynamicClientWithCustomListKinds`
|
||
|
||
**Docker (console/Dockerfile):**
|
||
- Multi-stage: `golang:1.26-alpine` → `alpine:3.20`
|
||
- Бинарник ~15MB
|
||
|
||
**K8s deployment (console/deploy/console.yaml):**
|
||
- ServiceAccount `fission-console`
|
||
- ClusterRole с доступом к fission.io CRD (get/list/create/update/patch/delete)
|
||
- ClusterRoleBinding
|
||
- Deployment (1 replica, port 8090, health probes, resource limits)
|
||
- Service (ClusterIP:8090)
|
||
- Ingress (fission.kube5s.ru/console, nginx, TLS)
|
||
|
||
#### Invoke auth — проблема и решение
|
||
|
||
**Проблема:** invoke через console возвращал 401 от Fission router.
|
||
|
||
**Исследование:**
|
||
1. Попробовал SA token из пода → `key is of invalid type` (K8s SA = RS256, router ожидает HS256)
|
||
2. Прочитал feature-config: `auth.enabled: true`, endpoint `/auth/login`, JWT expiry 120s
|
||
3. Нашёл credentials в секрете `router`: username=admin, password=..., jwtSigningKey=...
|
||
4. Протестировал: `POST /auth/login` → получил JWT → invoke с Bearer JWT → 200 OK
|
||
|
||
**Решение:** console при invoke делает POST `/auth/login` к роутеру, получает JWT, кеширует на 100s.
|
||
|
||
**Баги по пути:**
|
||
- Router возвращает 201 Created (не 200) на login — код проверял строго 200 → добавил `|| 201`
|
||
- Кешированный Docker-образ: пересобрал тот же тег v0.2.0, нод использовал старый → перешёл на v0.2.2
|
||
|
||
**Env переменные для auth:**
|
||
- `FISSION_AUTH_USERNAME` → secretKeyRef из `router.username`
|
||
- `FISSION_AUTH_PASSWORD` → secretKeyRef из `router.password`
|
||
|
||
#### E2E результат (все через ingress https://fission.kube5s.ru/console/)
|
||
|
||
| Операция | Результат |
|
||
|----------|-----------|
|
||
| health | 200 ok |
|
||
| create | 201 Created (function + package + httptrigger) |
|
||
| get | 200 (code, route, methods, environment) |
|
||
| update code | 200 |
|
||
| invoke | **200**, response_raw = ответ функции, latency_ms = 138 |
|
||
| delete | 200 (trigger + function + package очищены) |
|
||
|
||
#### Коммиты
|
||
|
||
- `6731e89` — feat: deliver fission console UI, k8s deploy, and tests
|
||
- `8920ba1` — fix: invoke auth via Fission router JWT login
|
||
|
||
**Docker Hub:** `naeel/fission-console:v0.2.2`
|
||
|
||
#### Правила репозитория
|
||
|
||
Создан `.github/copilot-instructions.md` с правилами:
|
||
|
||
### Обновление этапа (UI invoke stability: JS)
|
||
|
||
- Проведена диагностика JS invoke через реальный UI-путь (`/console/api/functions/{name}/invoke`) и роутер/экзекьютор логи.
|
||
- Найдена корневая причина JS-таймаутов:
|
||
- для `node-env` используется `v2/specialize`
|
||
- при `functionName=main` runtime пытался загрузить `/userfunc/deployarchive/main`
|
||
- при plain literal `deployarchive` является файлом, не директорией
|
||
- после specialize без корректного контракта запросы зависали и упирались в router roundtripper timeout.
|
||
- Для `nodejs-acc` зафиксирован runtime image: `ghcr.io/fission/node-env:1.32.5`.
|
||
- Для JS-функций выровнен контракт runtime:
|
||
- `spec.package.functionName` выставлен в пустой entrypoint (`""`) для default export
|
||
- код приведен к формату ответа Node runtime: `return { status: 200, body: "..." }`
|
||
- для основных JS-маршрутов включены методы `GET` + `POST`.
|
||
- Подтверждена работоспособность через console invoke:
|
||
- `fn-js-acc` -> `status=200`, `response_raw=hello-js-ok`
|
||
- `fn-js-direct` -> `status=200`, `response_raw=hello-js-ok`
|
||
- тестовая матрица `jsm1..jsm4` -> `status=200`.
|
||
|
||
### Статус после фикса
|
||
|
||
- Python invoke: работает.
|
||
- JS invoke: работает по UI-пути и по прямому GET роутов.
|
||
- Go invoke (`fn-go-acc`): остается отдельной runtime-проблемой (вне JS-фикса).
|
||
- Маппинг путей (локально ~/remote_dev/ = ВМ ~/terra/)
|
||
- Редактирование файлов — разрешено локально
|
||
- Команды — ИСКЛЮЧИТЕЛЬНО через SSH (VPN-конфликты)
|
||
|
||
### Следующий шаг
|
||
- UI: автоматическое обновление дашборда после create/delete
|
||
- Рассмотреть добавление логов функций (kubectl logs)
|
||
- Рассмотреть добавление time triggers в UI
|
||
|
||
---
|
||
|
||
## 2026-04-15 (дополнение) — Fix по результатам баг-репорта
|
||
|
||
### Что исправлено в Terraform provider
|
||
- `fission_package`: добавлен `ModifyPlan`, который автоматически пересчитывает `code_hash` по локальному коду (`source_dir`/`code_path`).
|
||
- `fission_package`: добавлена валидация существования `environment` до создания/обновления package.
|
||
- `fission_function`: добавлена валидация существования `environment` и `package` до create/update.
|
||
- `fission_function`: добавлена pre-flight валидация `entrypoint` для Python-исходника (`main.func` должен существовать как `def func(`).
|
||
- Добавлены unit-тесты на новый функционал (`code_hash`, entrypoint validation).
|
||
|
||
### Проверка на живом кластере
|
||
- После изменения `examples/hello-python/code/main.py` `terraform plan` теперь показывает `fission_package.hello will be updated in-place` с изменением `code_hash`.
|
||
- Конфигурация с несуществующим `environment` теперь падает на этапе apply с ошибкой валидации.
|
||
- Конфигурация с неверным `entrypoint` теперь падает на этапе apply с ошибкой валидации.
|
||
|
||
### Ограничения
|
||
- Runtime-проблемы Fission (зависания на cold start/таймауты выполнения) в этом изменении не трогались: исправлен только provider-слой валидации и обнаружения изменений.
|
||
|
||
## 2026-04-15 (дополнение) — Fix отображения кода в Console UI
|
||
|
||
### Проблема
|
||
- В модальном окне редактирования функции (`Edit Code`) для некоторых пакетов отображались байты ZIP (`PK...`) вместо исходного кода.
|
||
|
||
### Причина
|
||
- `GET /console/api/functions/{name}` декодировал `spec.deployment.literal` только из base64, но не обрабатывал архивированный payload.
|
||
|
||
### Исправление
|
||
- В `console/main.go` добавлено декодирование `literal` с поддержкой ZIP:
|
||
- если payload plain text/utf-8 -> отдаётся как есть
|
||
- если payload ZIP -> извлекается `main.py`/`main.js`/`main.go` (или первый utf-8 файл)
|
||
- Обновлён deployment image: `naeel/fission-console:v0.2.4`.
|
||
|
||
### Проверка
|
||
- `GET /console/api/functions/fn-go-acc` теперь возвращает читаемый Go-код, без `PK...` сигнатур.
|
||
|
||
## 2026-04-15 (дополнение) — Полный пользовательский прогон UI и invoke-fix
|
||
|
||
### Что проверено как пользовательский сценарий
|
||
- Прогнан массовый invoke через UI API (`POST /console/api/functions/{name}/invoke`) для всех функций в списке.
|
||
- Итого: `31` функций, из них `25` успешно отработали, `6` вернули 502/timeout (ожидаемо проблемные/сломанные кейсы).
|
||
- Проверен полный edit flow через UI API на рабочей функции:
|
||
- `GET function` -> `PUT /code` -> `POST /invoke` -> `PUT /code` (restore) -> `POST /invoke`.
|
||
|
||
### Найденный UI-баг и исправление
|
||
- Баг: после обновления кода через UI следующий invoke мог отдавать старую специализацию/кэш.
|
||
- Причина: обновлялся только `Package`, но не обновлялся `Function.spec.package.packageref.resourceversion`.
|
||
- Фикс в `console/main.go`:
|
||
- после `package update` читается новый `package.resourceVersion`
|
||
- выполняется update `Function` с новым `packageref.resourceversion`
|
||
- invoke сразу использует новую (или восстановленную) версию кода.
|
||
|
||
### Проверка фикса
|
||
- После update через UI invoke возвращает новый ответ.
|
||
- После restore через UI invoke возвращает исходный ответ (без зависания старого кэша).
|
||
|
||
### Статус `fn-go-acc`
|
||
- `fn-go-acc` продолжает падать не из-за UI, а из-за runtime specialization на стороне Fission.
|
||
- Подтверждено логами `router/executor`: `GetServiceForFunction ... context canceled` и постоянными readiness-fail у poolmgr pod-ов `go-acc`.
|
||
|
||
## 2026-04-15 (дополнение) — Финальный фикс `fn-go-acc`
|
||
|
||
### Симптом и root cause
|
||
- `fn-go-acc` стабильно timeout'ился на invoke.
|
||
- Изначальный пакет содержал `main.go` как `deployment.literal`, из-за чего go-runtime пытался грузить текст как plugin:
|
||
- `plugin.Open("/userfunc/deployarchive/main.go"): invalid ELF header`.
|
||
|
||
### Что сделано
|
||
- Пересобран deploy-артефакт как Go plugin (`main.so`) и упакован в `deploy.zip`.
|
||
- Важно: сборка выполнена в том же образе, что у Fission environment builder:
|
||
- `ghcr.io/fission/go-builder` (Go 1.25.6), чтобы избежать несовместимости plugin ABI.
|
||
- Функция обновлена через Fission CLI:
|
||
- `fission function update -n default --name fn-go-acc --env go-acc --entrypoint Handler --deployarchive /tmp/fn-go-acc-fix2/deploy.zip -f`.
|
||
|
||
### Результат проверки
|
||
- `/go-acc` через router с JWT: `hello-go-ok`, `HTTP 200`.
|
||
- Executor логи: specialization проходит успешно (`specialized pod`, `added function service`), без `invalid ELF`.
|
||
|
||
### Контрольный smoke после фикса
|
||
- `/auto/ok` -> `HTTP 200`
|
||
- `/js-acc` -> `HTTP 200`
|
||
- `/js-direct` -> `HTTP 200`
|
||
- `/go-acc` -> `HTTP 200`
|
||
|
||
## 2026-04-15 (дополнение) — Edit Code для `fn-go-acc` снова показывает исходник
|
||
|
||
### Проблема
|
||
- В `Edit Code` для `fn-go-acc` поле `code` было пустым.
|
||
- Причина: после `--deployarchive` пакет `pkg-go-acc` перешел на `spec.deployment.type=url`, а в console backend чтение кода шло только из `spec.deployment.literal`.
|
||
|
||
### Исправление
|
||
- В `console/main.go` добавлен fallback-поиск исходника:
|
||
- `spec.source.literal`
|
||
- `spec.deployment.literal`
|
||
- `spec.source.url`
|
||
- `spec.deployment.url`
|
||
- Добавлена загрузка архива по URL и извлечение исходника из zip (если есть текстовые файлы).
|
||
- Добавлен unit-тест `TestGetFunctionUsesSourceLiteralWhenDeploymentLiteralMissing`.
|
||
- Собран и выкачен образ `naeel/fission-console:v0.2.6`, деплой обновлен.
|
||
|
||
### Дополнительно по данным в кластере
|
||
- Для `fn-go-acc` обновлен `sourcearchive` (`main.go`), чтобы `pkg-go-acc.spec.source.literal` содержал исходник и был доступен в Edit Code.
|
||
|
||
### Проверка
|
||
- `GET /console/api/functions/fn-go-acc` возвращает непустой `code` (Go source).
|
||
- `/go-acc` продолжает отвечать `hello-go-ok`, `HTTP 200`.
|
||
|
||
## 2026-04-15 (дополнение) — TLS в console, бренд NUBES, проверка `tf-neg-syntax-fn`
|
||
|
||
### TLS: фактический статус
|
||
- `http://fission.kube5s.ru/console/` -> `308 Permanent Redirect` на `https://...`
|
||
- `https://fission.kube5s.ru/console/` -> `200`
|
||
- Ingress `fission-console` настроен с:
|
||
- `nginx.ingress.kubernetes.io/force-ssl-redirect: "true"`
|
||
- `tls.secretName: fission-tls`
|
||
- `cert-manager.io/cluster-issuer: letsencrypt-prod`
|
||
|
||
### UI оформление (NUBES)
|
||
- В `console/ui/index.html` добавлены:
|
||
- favicon (inline SVG)
|
||
- брендинг в navbar: mark + wordmark `NUBES` / `FISSION CONSOLE`
|
||
- обновлен `<title>` на `NUBES Fission Console`
|
||
- Выкат: `naeel/fission-console:v0.2.9`.
|
||
|
||
### Invoke `tf-neg-syntax-fn` — проверка
|
||
- Подтверждено, что у функции синтаксически невалидный код (`def main(: ...`).
|
||
- Console invoke теперь возвращает fail-fast ошибку (не «молчание»):
|
||
- `{"error":"invoke \"tf-neg-syntax-fn\" timeout after 20s: function specialization likely failed (for example, syntax error)"}`
|
||
- Причина на кластере: specialization падает в runtime с `500`, executor делает ретраи.
|
||
|
||
## 2026-04-15 (дополнение) — Лечение "красного" HTTPS
|
||
|
||
### Root cause
|
||
- На одном host `fission.kube5s.ru` было два ingress:
|
||
- `fission-console` с TLS
|
||
- `fission-router` без TLS
|
||
- Из-за смешанной host-конфигурации TLS мог работать нестабильно/давать красный индикатор в браузере.
|
||
|
||
### Fix
|
||
- Пропатчен `fission/fission-router`:
|
||
- добавлен `spec.tls` с `secretName: fission-tls`
|
||
- добавлена аннотация `nginx.ingress.kubernetes.io/force-ssl-redirect: "true"`
|
||
|
||
### Проверка
|
||
- `http://fission.kube5s.ru/neg/syntax` -> `308` redirect на HTTPS
|
||
- Сертификат endpoint: Let's Encrypt R13, CN/SAN `fission.kube5s.ru`, валиден
|
||
- `https://fission.kube5s.ru/console/` -> `HTTP 200`
|
||
|
||
## 2026-04-15 (дополнение) — Строгая очистка: оставлены только рабочие и с видимым кодом
|
||
|
||
### Требование
|
||
- Убрать всё лишнее и оставить только функции, которые одновременно:
|
||
- имеют непустой `code` в `GET /console/api/functions/{name}`
|
||
- успешно проходят `POST /console/api/functions/{name}/invoke` со `status=200`
|
||
|
||
### Что сделано
|
||
- Выполнена финальная очистка функций по whitelist.
|
||
- После очистки дополнительно проверены все оставшиеся функции через Console API (code + invoke).
|
||
- Исправлена ошибка в служебном cleanup-скрипте (битый jsonpath с `{n}`), из-за которой падала пост-обработка orphan-ресурсов.
|
||
|
||
### Итоговый набор
|
||
- `auth-test2`
|
||
- `fn-js-direct`
|
||
- `fn-js-acc`
|
||
- `hello`
|
||
- `tf-auto-ok-fn`
|
||
- `tf-stress-fast-fn`
|
||
|
||
### Финальное состояние кластера
|
||
- `functions=6`
|
||
- `httptriggers=6`
|
||
- `packages=6`
|
||
|
||
### Финальная валидация
|
||
- Для каждой из 6 функций: `code=OK`, `invoke=OK`.
|
||
|
||
## 2026-04-15 (дополнение) — Наращивание до 15 функций + исправление фавикона в live UI
|
||
|
||
### Запрос
|
||
- Увеличить набор до 15 функций, включая error-кейсы.
|
||
- Прогнать тесты и чинить код/манифесты при расхождении с ожиданием.
|
||
- Исправить favicon в реальном UI (а не только в репозитории).
|
||
|
||
### Что сделано по функциям
|
||
- Подняты функции из `examples`: `hello-python`, `deep-recursion`, `destroy-test`, `frequent-update`, `orphan-test`, `multi-env-1/2/3`.
|
||
- Добавлен негативный кейс `tf-neg-syntax-fn` с route `/neg/syntax`.
|
||
- Доведено до ровно `functions=15`, `httptriggers=15`.
|
||
|
||
### Фиксы по коду/манифестам (по результатам тестов)
|
||
- `examples/multi-env-1/main.tf`
|
||
- исправлен `source_dir` с `"$\{path.module\}/code"` на `"${path.module}/code"`.
|
||
- заменен image env с `ghcr.io/fission/python-env:v1.20.0` на `ghcr.io/fission/python-env`.
|
||
- `examples/multi-env-2/main.tf`
|
||
- аналогичные исправления `source_dir` и image.
|
||
- `examples/multi-env-3/main.tf`
|
||
- аналогичные исправления `source_dir` и image.
|
||
|
||
### Тест-матрица (router + JWT)
|
||
- Итог: `PASS 15 / FAIL 0`.
|
||
- Успешные (`HTTP 200`):
|
||
- `/auth-test2`, `/js-acc`, `/js-direct`, `/hello`, `/auto/ok`, `/destroy-test`, `/freq-update`, `/tf-hello`, `/multi-env-1`, `/multi-env-2`, `/multi-env-3`, `/orphan-test`, `/stress/fast`.
|
||
- Ожидаемые error-кейсы:
|
||
- `/deep-recursion` -> timeout (`curl rc=28`, `HTTP 000`)
|
||
- `/neg/syntax` -> timeout (`curl rc=28`, `HTTP 000`)
|
||
- Доп. проверка через Console invoke:
|
||
- `tf-multi-env-1-fn` -> `status=200`
|
||
- `tf-hello-fn` -> `status=200`
|
||
- `tf-neg-syntax-fn` и `tf-deep-recursion-fn` -> ожидаемая fail-fast ошибка invoke timeout.
|
||
|
||
### Фавикон (live)
|
||
- Причина «старого фавикона»: в кластере работал старый образ `naeel/fission-console:v0.3.0`.
|
||
- Собран и выкачен новый образ: `naeel/fission-console:v0.3.1`.
|
||
- Deployment обновлен и успешно прокатан.
|
||
- Проверено в live HTML: отдается
|
||
- `<link rel="icon" type="image/png" href="https://nubes.ru/themes/custom/nubes_2025/favicon.png">`.
|
||
---
|
||
|
||
## 2026-04-27
|
||
|
||
### Сессия: poolmgr reuse fix (Bug #1 + Bug #2) + console cleanup
|
||
|
||
#### Bug #1 — Generation в TapService/UnTapService (`client.go`, commit `775845b`)
|
||
- Симптом: 10/10 HTTP 200, но pod count рос 1→11 (новый pod на каждый invoke).
|
||
- Root cause: `Generation` не передавался в `TapService`/`UnTapService`. `MarkAvailable` строил ключ `{UID, RV, Generation=0}` вместо `{UID, RV, Generation=1}` → `activeRequests` никогда не декрементировался → всегда cache miss.
|
||
- Fix: добавить `Generation: fnMeta.Generation` в оба вызова в `pkg/executor/client/client.go`.
|
||
- Image: `naeel/fission-bundle:v1.22.0-multi-ns-9`.
|
||
|
||
#### Bug #2 — `gpm.podLister[ns]` nil для user namespace (`gpm.go`, commit `0cae275`)
|
||
- Симптом: после Bug #1 fix — reuse всё ещё не работал.
|
||
- Root cause: `AddNamespace` обновлял только `poolPodC.podLister[ns]`, но не `gpm.podLister[ns]`. `IsValid` вызывал `gpm.podLister[ns].Pods(...).Get(...)` → nil pointer panic → net/http рвал соединение → router retry → `activeRequests=1` (stuck) → cache miss → новый pod.
|
||
- Fix: добавить одну строку в `AddNamespace` в `pkg/executor/executortype/poolmgr/gpm.go`:
|
||
```go
|
||
gpm.podLister[ns] = gpmInformer.Core().V1().Pods().Lister()
|
||
```
|
||
- Image: `naeel/fission-bundle:v1.22.0-multi-ns-10`.
|
||
- Live validation: `PHP_PODS_BEFORE=1 → PHP_PODS_AFTER=2, NEW_PODS_CREATED=1, REUSE_WORKING=YES_REUSE_FIXED`.
|
||
- Postmortem: `doc/POOLMGR_REUSE_BUG_POSTMORTEM_2026-04-27.md`.
|
||
|
||
#### Console: убрана карточка тайм-триггеров (`console/ui/index.html`, commit `96a02b4`)
|
||
- Убрана карточка "Тайм-триггеры" из dashboard (счётчик `0`).
|
||
- Убраны JS-строки: `getJSON('/timetriggers')`, `setText('time-count', ...)`, `const [..., time]`.
|
||
- Образ: `naeel/fission-console:v1.1` (sha256:0713fee1...).
|
||
- Деплой: `kubectl set image deployment/fission-console console=naeel/fission-console:v1.1 -n fission`.
|
||
- **Важно**: container name в deployment = `console` (не `fission-console`!).
|
||
|
||
#### TODO: тайм-триггеры (не реализовано, отложено)
|
||
Нужно реализовать:
|
||
1. Поле "Cron" в форме создания функции (или отдельная форма/таблица)
|
||
2. `POST /console/api/timetriggers` → handler `handleCreateTimeTrigger`
|
||
3. `DELETE /console/api/timetriggers/:name` → handler
|
||
4. `model.CreateTimeTriggerRequest {Name, FunctionName, Cron string}`
|
||
5. CRD builder: `fission.io/v1` kind `TimeTrigger`, spec: `{cron, functionref: {name, namespace}}`
|
||
6. Таблица тайм-триггеров в UI с колонками: имя, функция, расписание, удалить
|
||
|
||
#### Текущее состояние live кластера
|
||
- Fission bundle: `naeel/fission-bundle:v1.22.0-multi-ns-10` (executor + router)
|
||
- Console: `naeel/fission-console:v1.1`
|
||
- User namespace: `fission-ffd1f598c169b0ae`
|
||
- Функции: `88` (Python), `88node` (Node.js), `phppp` (PHP), `rub` (Ruby)
|
||
- Маршруты: `/f598c169b0ae/{name}`
|
||
- SSH: `naeel@5.172.178.213`, key `~/.ssh/naeel_vm_id_ed25519`
|
||
- Token: `grep -m1 "^api_token" /home/naeel/terra/terraform/TEST_STAND/POSTGRES/terraform.tfvars | sed -E "s/^api_token = \"(.*)\"$/\1/"` |