Compare commits

...
8 Commits
10 changed files with 717 additions and 4 deletions
+5 -1
View File
@@ -46,7 +46,7 @@ spec:
serviceAccountName: fission-console serviceAccountName: fission-console
containers: containers:
- name: console - name: console
image: naeel/fission-console:v0.2.4 image: naeel/fission-console:v0.3.4
ports: ports:
- containerPort: 8090 - containerPort: 8090
env: env:
@@ -56,6 +56,10 @@ spec:
value: "http://router.fission.svc.cluster.local" value: "http://router.fission.svc.cluster.local"
- name: PORT - name: PORT
value: "8090" value: "8090"
- name: FISSION_HTTP_TIMEOUT
value: "30s"
- name: FISSION_INVOKE_TIMEOUT
value: "20s"
- name: FISSION_AUTH_USERNAME - name: FISSION_AUTH_USERNAME
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
+17 -1
View File
@@ -430,7 +430,23 @@ func (s *server) handleUpdateFunctionCode(w http.ResponseWriter, r *http.Request
return return
} }
writeAnyJSON(w, http.StatusOK, map[string]any{"updated": true, "package": pkgName}) updatedPkg, err := s.dyn.Resource(packageGVR).Namespace(s.ns).Get(ctx, pkgName, metav1.GetOptions{})
if err != nil {
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("get updated package %q: %v", pkgName, err))
return
}
if err := unstructured.SetNestedField(fn.Object, updatedPkg.GetResourceVersion(), "spec", "package", "packageref", "resourceversion"); err != nil {
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("set function package resourceversion: %v", err))
return
}
if _, err := s.dyn.Resource(functionGVR).Namespace(s.ns).Update(ctx, fn, metav1.UpdateOptions{}); err != nil {
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("update function %q package ref: %v", name, err))
return
}
writeAnyJSON(w, http.StatusOK, map[string]any{"updated": true, "package": pkgName, "package_resourceversion": updatedPkg.GetResourceVersion()})
} }
func (s *server) handleInvokeFunction(w http.ResponseWriter, r *http.Request, name string) { func (s *server) handleInvokeFunction(w http.ResponseWriter, r *http.Request, name string) {
+53 -2
View File
@@ -3,7 +3,13 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fission Console</title> <title>NUBES Fission Console</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA4ZJREFUeJztm8+Lm0UYxz/fySZUoWIrVtbmXfyBP05Fa3e3rogsglJRK0p7E2svnrz6B3gQ8eRJFooXET14KKJ4FUQo3UQQ9CRC6W4WQW0VXZduTd7HQ9c22bzbTCY/ZpO8n0syb56Z+c437zPvhMzIzJhkXGwBsckNiC0gNrkBsQXEZsonSJLj4GNPgb2ACo9j9gDOvrSV6qkB6+sJ6WSJ5MIxUo4h5sDuI9U7tlZ57/+YTAN0zyOPUi+9jOxJxMOUj9wJOBBg117QvqGMwhNpcQ/J+rOkPIc0j+xeytyGsaWXa2+kvc312gxQMleHYgFtrQ9GYJmgmSfup3z15xuDNW/dGXOAjd680NgshlYdvcH2mfEwQIU0tOp4GNADuQGxBcRmPAyQgh/W42FAD+QGxBYQm/EwIF8HhJMbEFtAX3DreQqEkhsQW0BsxsMAlz8Gg8kNiC0gNuNhgJvK54BQcgNiC+gLhUlPgcuTbkAP5AbEFhCbcAOM4LyLitIW3eEGyP7qWUwMHFdaixNO1v4Av5pG8H/yfWfvesk7tq5GczHDAHnmtu727nTQbJYe8o51+qOl2BYgNv1a0iHvTgeNGq94x6buYnOx3YCU3/1asn06ePQZ744HiXTCO3a6/m1zsd0Ax/f+HTc+kU76598AUDL/LpjfjjWxYdXqRvOljDvAfezfO3dQvvCDtLjHu04f0czsaSx9y7uC8eP2S20GWO38Z+A7DwDwIOX135TMvylpKI9VJQv7lcx+hfEhurELsCPOfdDWVtZ5ASWzZ4GXArQ1QJfA/kTW6BhtLgX+7qL9IqTTmKa7GjgAumKry7dsv5q9Vfbfq29QLL1I9wulAtgB4ADmoy9kY4fodugAODuT2dpOJ0aUzH8E6asBXe0+zP5h7bvbzay+/aOdv+Ha8mnE5YEKGxqF17MGDzcxwMzqTBUWgc65vKuxT7cm9kx2TIHrATOzxzHOEpZ5sanYamXuZgEdJzlbqXyOmzrO6N0J56hVj3YK8prl7eK5L6g3DmFc6l3XEDCWbLWyYGYdf9h1TIGWYMmRHDlDaqcY0qKnO/Qr4oStLH/jXSPk4KSShf2ovoTZ80Db4iICv2D2ttWqS91WDDKgpYHy4adR8TWww8BdmN2KrOg5Zwoo+PV0/ThICmyArWHua9D7Vjv/U5j6Phgw6uzCPB4uuQGxBcRm4g34D1IN9ODV8oQpAAAAAElFTkSuQmCC">
<script>
if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
window.location.replace('https://' + window.location.host + window.location.pathname + window.location.search + window.location.hash);
}
</script>
<style> <style>
:root { :root {
--bg-page: #001120; --bg-page: #001120;
@@ -39,6 +45,45 @@
letter-spacing: .3px; letter-spacing: .3px;
} }
.brand {
display: flex;
align-items: center;
gap: 10px;
}
.brand-mark {
width: 30px;
height: 30px;
border-radius: 8px;
background: linear-gradient(135deg, #009dff 0%, #0055d4 100%);
color: #fff;
display: grid;
place-items: center;
font-size: 14px;
font-weight: 800;
letter-spacing: .02em;
box-shadow: 0 6px 18px rgba(0, 125, 255, .35);
}
.brand-text {
display: flex;
flex-direction: column;
line-height: 1.08;
}
.brand-text .nubes {
font-size: 12px;
letter-spacing: .22em;
font-weight: 800;
color: #8bc7ff;
}
.brand-text .product {
font-size: 13px;
font-weight: 700;
letter-spacing: .04em;
}
.btn { .btn {
background: var(--accent); background: var(--accent);
color: #fff; color: #fff;
@@ -243,7 +288,13 @@
</head> </head>
<body> <body>
<div class="navbar"> <div class="navbar">
<div class="title">sless / Fission Console</div> <div class="brand">
<div class="brand-mark">N</div>
<div class="brand-text">
<div class="nubes">NUBES</div>
<div class="product">FISSION CONSOLE</div>
</div>
</div>
<div class="row" style="margin:0;"> <div class="row" style="margin:0;">
<button class="btn ghost" onclick="reloadAll()">Refresh</button> <button class="btn ghost" onclick="reloadAll()">Refresh</button>
<button class="btn" onclick="openCreate()">+ Create Function</button> <button class="btn" onclick="openCreate()">+ Create Function</button>
+204
View File
@@ -258,6 +258,30 @@
#### Правила репозитория #### Правила репозитория
Создан `.github/copilot-instructions.md` с правилами: Создан `.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/) - Маппинг путей (локально ~/remote_dev/ = ВМ ~/terra/)
- Редактирование файлов — разрешено локально - Редактирование файлов — разрешено локально
- Команды — ИСКЛЮЧИТЕЛЬНО через SSH (VPN-конфликты) - Команды — ИСКЛЮЧИТЕЛЬНО через SSH (VPN-конфликты)
@@ -302,3 +326,183 @@
### Проверка ### Проверка
- `GET /console/api/functions/fn-go-acc` теперь возвращает читаемый Go-код, без `PK...` сигнатур. - `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">`.
+271
View File
@@ -147,3 +147,274 @@ User → POST /console/api/functions/NAME/invoke
Причина: VPN может быть активен локально, вызывая сбои при прямом выполнении команд. Причина: VPN может быть активен локально, вызывая сбои при прямом выполнении команд.
Монтирование sshfs делает локальное редактирование файлов эквивалентным редактированию на ВМ. Монтирование sshfs делает локальное редактирование файлов эквивалентным редактированию на ВМ.
---
## Сессия: JS invoke timeouts в UI (root cause + fix)
### Симптом
- Все JS invoke через UI (`/console/api/functions/{name}/invoke`) падали по timeout.
- В router логах: `roundtripper timeout (60s)`/`error sending request to function`.
- В executor логах specialization для node окружения могла проходить, но ответ функции не возвращался.
### Ключевые находки
1. Node runtime работал через `POST /v2/specialize`.
2. При `functionName=main` runtime строил путь загрузки как:
- `/userfunc/deployarchive/main`
3. В реальном поде `/userfunc/deployarchive` был **файлом** (literal payload), а не директорией.
4. После перевода на пустой entrypoint (`functionName=""`) specialization стала успешной (`202`, `user code loaded`),
но запросы всё равно зависали.
5. Прямая проверка контейнера показала второй контрактный нюанс node runtime:
- пользовательская функция должна возвращать объект формата
`{ status, body, headers? }`
- возврат простой строки приводил к зависанию ответа (callback не отправлял HTTP response).
### Принятые изменения
- `nodejs-acc` закреплен на `ghcr.io/fission/node-env:1.32.5`.
- Для JS функций (`fn-js-acc`, `fn-js-direct`, тестовые `jsm1..jsm4`):
- выставлен пустой entrypoint (`functionName=""`)
- код приведен к контракту Node runtime:
- `module.exports = async function(context) { return { status: 200, body: "..." }; }`
- Для основных JS trigger включены методы `GET` и `POST`.
### Проверка
- UI invoke:
- `fn-js-acc` -> `status=200`, `response_raw=hello-js-ok`
- `fn-js-direct` -> `status=200`, `response_raw=hello-js-ok`
- Прямые GET через роутер:
- `/js-acc` -> 200
- `/js-direct` -> 200
- Тестовые `jsm1..jsm4` после унификации кода -> 200 через UI invoke.
### Вывод
Проблема была не в UI-рендеринге и не в одном конкретном trigger, а в несовпадении контракта Node runtime:
- entrypoint/path specialization
- shape возвращаемого значения функции.
---
## Сессия: Финальный fix `fn-go-acc` (Go runtime specialization)
### Симптом
- `fn-go-acc` выдавал timeout на invoke через console/router.
### Диагностика
1. На исходной конфигурации в executor логах:
- `plugin.Open("/userfunc/deployarchive/main.go"): invalid ELF header`.
2. Это означало, что runtime ожидал plugin-артефакт, но в package deployment лежал исходник `main.go`.
3. После первой попытки собрать plugin локальным `go1.26.1` ошибка `invalid ELF` ушла, но появился новый фейл specialization:
- `Post "http://127.0.0.1:8888/v2/specialize": EOF`.
4. Вывод: plugin собран несовместимой версией Go относительно runtime.
### Решение
- Сборка plugin выполнена в `ghcr.io/fission/go-builder` (Go 1.25.6), то есть тем же toolchain, что у Fission environment builder:
- `GO111MODULE=off /usr/local/go/bin/go build -buildmode=plugin -o main.so ./main.go`
- упаковка `main.so` в `deploy.zip`.
- Обновление функции через 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` и без `EOF`.
### Контрольная матрица маршрутов
- `/auto/ok` -> 200
- `/js-acc` -> 200
- `/js-direct` -> 200
- `/go-acc` -> 200
### Практический вывод
Для go-env в этом кластере критично соблюдать контракт runtime:
1. deploy-пакет должен содержать именно plugin (`.so`), а не исходник.
2. plugin должен быть собран совместимой версией Go (через `ghcr.io/fission/go-builder`).
---
## Сессия: `Edit Code` для `fn-go-acc` пустой
### Симптом
- В UI (`GET /console/api/functions/fn-go-acc`) поле `code` было пустым.
### Почему так произошло
- После фикса runtime функция была переведена на deploy-архив plugin (`main.so`).
- Package имел вид:
- `spec.deployment.type=url`
- `spec.deployment.url=...`
- без `spec.deployment.literal`
- Backend в `handleGetFunction` извлекал код только из `spec.deployment.literal`, поэтому для URL-пакета отдавал пустую строку.
### Что изменено
- В backend добавлен `extractPackageSourceCode(...)` с fallback-приоритетом:
1. `spec.source.literal`
2. `spec.deployment.literal`
3. `spec.source.url`
4. `spec.deployment.url`
- Добавлена загрузка URL-архива через `fetchPackageArchive(...)` и декодирование через `decodeArchiveBytesToSource(...)`.
- Обновлен unit-тестами сценарий, где `deployment.literal` отсутствует, но есть `source.literal`.
### Операционные шаги
- Собран и задеплоен `naeel/fission-console:v0.2.6`.
- Для текущей `fn-go-acc` дополнительно обновлен `sourcearchive` (`main.go`), чтобы исходник гарантированно отображался в Edit Code.
### Результат
- `GET /console/api/functions/fn-go-acc` теперь возвращает непустой Go source в `code`.
- Вызов `/go-acc` остается рабочим (`hello-go-ok`, HTTP 200).
---
## Сессия: Проверка TLS + branding NUBES + верификация `tf-neg-syntax-fn`
### TLS
- Проверен доступ к console:
- `http://.../console/` -> `308` redirect на HTTPS
- `https://.../console/` -> `200`
- Ingress конфиг содержит корректный TLS блок и принудительный SSL redirect.
- Вывод: endpoint защищен TLS, «незащищенного HTTP-доступа» нет.
### Брендинг NUBES
- В `console/ui/index.html` добавлены:
- `<title>NUBES Fission Console</title>`
- favicon (inline SVG)
- navbar brand block с `NUBES` / `FISSION CONSOLE`.
- Обновлен образ до `naeel/fission-console:v0.2.9`.
### `tf-neg-syntax-fn` — почему timeout вместо прямого SyntaxError
- Функция содержит заведомо битый Python (`def main(:`), confirmed через `GET /console/api/functions/tf-neg-syntax-fn`.
- На runtime это роняет specialization (`500`), после чего executor ретраит.
- Поэтому router/console не получает «чистый traceback» сразу из runtime API и видит timeout/ошибку specialization.
- Для UX введен fail-fast и явная ошибка в console invoke:
- `timeout after 20s: function specialization likely failed (for example, syntax error)`.
---
## Сессия: "Не защищено красным" — экстренное лечение
### Наблюдение
- Сертификат endpoint сам по себе валидный (Let's Encrypt, CN/SAN = `fission.kube5s.ru`).
- Но на том же host были два ingress с разной TLS-конфигурацией:
- `fission-console` с TLS
- `fission-router` без TLS
### Действие
- Патч `fission-router`:
- добавлен `spec.tls` с `secretName: fission-tls`
- добавлен `force-ssl-redirect=true`
### Результат
- HTTP на роутер-пути дает 308 -> HTTPS
- HTTPS на console стабильно 200
- Конфигурация host выровнена: теперь и console, и router на одном сертификате.
---
## Сессия: Строгая чистка до "только точно рабочие и видимые"
### Запрос
- Оставить только функции, которые в UI/Console:
- показывают исходник (`code` не пустой)
- реально вызываются (`invoke.status == 200`)
### Что произошло
- Выполнен массовый cleanup с keep-list.
- По логу cleanup выявилась ошибка jsonpath в пост-обработке orphan-пакетов:
- использовалось `{n}` вместо `{"\\n"}`
- это ломало шаг удаления orphan-объектов после основного удаления.
- Основное удаление функций при этом сработало корректно.
### Проверка после cleanup
- Текущее состояние:
- functions: 6
- httptriggers: 6
- packages: 6
- Остались только:
- `auth-test2`
- `fn-js-direct`
- `fn-js-acc`
- `hello`
- `tf-auto-ok-fn`
- `tf-stress-fast-fn`
- Для каждой функции через Console API подтверждено:
- `code=OK`
- `invoke=OK`
### Вывод
- Финальный набор теперь соответствует строгому критерию "однозначно работает и показывается".
---
## Сессия: Возврат к 15 функциям + проверка ожиданий + live favicon
### Наблюдение в начале
- После строгой чистки в кластере оставалось 6 функций.
- В live UI favicon оставался старым, хотя в репозитории правка уже была.
- Проверка deployment показала: работал старый image `naeel/fission-console:v0.3.0`.
### Расширение набора до 15
- Добавлены функции из примеров:
- `tf-hello-fn`, `tf-deep-recursion-fn`, `tf-destroy-test-fn`, `tf-freq-update-fn`, `tf-orphan-fn`
- `tf-multi-env-1-fn`, `tf-multi-env-2-fn`, `tf-multi-env-3-fn`
- Добавлен негативный кейс:
- `tf-neg-syntax-fn` + route `/neg/syntax`
- Итоговый размер:
- functions = 15
- httptriggers = 15
### Что сломалось и как починено
1. `multi-env-1/2/3` не применялись:
- ошибка Terraform: `Invalid escape sequence` из-за `"$\{path.module\}/code"`.
- фикс: заменено на `"${path.module}/code"` во всех трех `main.tf`.
2. После применения `multi-env-1/2/3` маршруты таймаутили (`rc=28`):
- причина: image env `ghcr.io/fission/python-env:v1.20.0`.
- фикс: переключено на `ghcr.io/fission/python-env`, re-apply всех трех модулей.
- результат: `/multi-env-1`, `/multi-env-2`, `/multi-env-3` -> `HTTP 200`.
### Финальная тест-матрица
- Router tests с JWT: `PASS=15, FAIL=0`.
- Успешные 200: базовые + multi-env + tf-hello + stress-fast.
- Ожидаемые ошибки:
- `/deep-recursion` -> timeout (`HTTP 000`, `rc=28`)
- `/neg/syntax` -> timeout (`HTTP 000`, `rc=28`)
- Console invoke подтверждает:
- positive функции -> `status=200`
- negative функции -> fail-fast timeout error.
### Favicon в live
- Собран и выкачен `naeel/fission-console:v0.3.1`.
- `deployment/fission-console` обновлен и rollout успешен.
- Проверка `https://fission.kube5s.ru/console/` показывает нужный favicon URL:
- `https://nubes.ru/themes/custom/nubes_2025/favicon.png`.
+11
View File
@@ -0,0 +1,11 @@
package main
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "Hello from Go in Fission")
}
+39
View File
@@ -0,0 +1,39 @@
terraform {
required_providers {
fission = {
source = "nail/fission"
version = "~> 0.1.0"
}
}
}
provider "fission" {
kubeconfig_path = "/home/naeel/.kube/config"
namespace = "default"
}
resource "fission_environment" "go" {
name = "tf-go-hello-env"
image = "ghcr.io/fission/go-env"
version = 3
}
resource "fission_package" "pkg" {
name = "tf-go-hello-pkg"
environment = fission_environment.go.name
source_dir = "${path.module}/code"
}
resource "fission_function" "fn" {
name = "tf-go-hello-fn"
environment = fission_environment.go.name
package_name = fission_package.pkg.name
entrypoint = "main.Handler"
}
resource "fission_http_trigger" "route" {
name = "tf-go-hello-route"
function = fission_function.fn.name
url = "/go-hello"
methods = ["GET"]
}
+39
View File
@@ -0,0 +1,39 @@
terraform {
required_providers {
fission = {
source = "nail/fission"
version = "~> 0.1.0"
}
}
}
provider "fission" {
kubeconfig_path = "/home/naeel/.kube/config"
namespace = "default"
}
resource "fission_environment" "python" {
name = "tf-multi-env-1"
image = "ghcr.io/fission/python-env"
version = 3
}
resource "fission_package" "pkg" {
name = "tf-multi-env-1-pkg"
environment = fission_environment.python.name
source_dir = "${path.module}/code"
}
resource "fission_function" "fn" {
name = "tf-multi-env-1-fn"
environment = fission_environment.python.name
package_name = fission_package.pkg.name
entrypoint = "main.main"
}
resource "fission_http_trigger" "route" {
name = "tf-multi-env-1-route"
function = fission_function.fn.name
url = "/multi-env-1"
methods = ["GET"]
}
+39
View File
@@ -0,0 +1,39 @@
terraform {
required_providers {
fission = {
source = "nail/fission"
version = "~> 0.1.0"
}
}
}
provider "fission" {
kubeconfig_path = "/home/naeel/.kube/config"
namespace = "default"
}
resource "fission_environment" "python" {
name = "tf-multi-env-2"
image = "ghcr.io/fission/python-env"
version = 3
}
resource "fission_package" "pkg" {
name = "tf-multi-env-2-pkg"
environment = fission_environment.python.name
source_dir = "${path.module}/code"
}
resource "fission_function" "fn" {
name = "tf-multi-env-2-fn"
environment = fission_environment.python.name
package_name = fission_package.pkg.name
entrypoint = "main.main"
}
resource "fission_http_trigger" "route" {
name = "tf-multi-env-2-route"
function = fission_function.fn.name
url = "/multi-env-2"
methods = ["GET"]
}
+39
View File
@@ -0,0 +1,39 @@
terraform {
required_providers {
fission = {
source = "nail/fission"
version = "~> 0.1.0"
}
}
}
provider "fission" {
kubeconfig_path = "/home/naeel/.kube/config"
namespace = "default"
}
resource "fission_environment" "python" {
name = "tf-multi-env-3"
image = "ghcr.io/fission/python-env"
version = 3
}
resource "fission_package" "pkg" {
name = "tf-multi-env-3-pkg"
environment = fission_environment.python.name
source_dir = "${path.module}/code"
}
resource "fission_function" "fn" {
name = "tf-multi-env-3-fn"
environment = fission_environment.python.name
package_name = fission_package.pkg.name
entrypoint = "main.main"
}
resource "fission_http_trigger" "route" {
name = "tf-multi-env-3-route"
function = fission_function.fn.name
url = "/multi-env-3"
methods = ["GET"]
}