Compare commits
154
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33bc765f99 | ||
|
|
7265985309 | ||
|
|
650616b464 | ||
|
|
c8ced44068 | ||
|
|
491f0aee43 | ||
|
|
2a7d6101b1 | ||
|
|
f5b57173f5 | ||
|
|
919e84396c | ||
|
|
28c45e65aa | ||
|
|
695bfb74d4 | ||
|
|
4eedf95f5c | ||
|
|
3b93c5dc8b | ||
|
|
4c82285863 | ||
|
|
728cc351b5 | ||
|
|
5d52c9ba94 | ||
|
|
5f0ab79f00 | ||
|
|
4addf254cb | ||
|
|
b5f8a9bf0e | ||
|
|
b2efefd75a | ||
|
|
5988ced1e2 | ||
|
|
e3928e1d4d | ||
|
|
63ce6ea135 | ||
|
|
7b6ff84188 | ||
|
|
55d0b5a9e7 | ||
|
|
813617ffd1 | ||
|
|
7d7fe561a8 | ||
|
|
fed69da335 | ||
|
|
9f0e911b9d | ||
|
|
f4a3bffc6b | ||
|
|
90924cdec7 | ||
|
|
6e037a506d | ||
|
|
d24605a8b8 | ||
|
|
d2ff55f9e0 | ||
|
|
b9236698f3 | ||
|
|
073f2c1504 | ||
|
|
b93e720e12 | ||
|
|
f16aa030db | ||
|
|
5c481b7293 | ||
|
|
67db8d71f1 | ||
|
|
2bbed95c2a | ||
|
|
a1517ba4b2 | ||
|
|
49be1db3a0 | ||
|
|
c3b161da83 | ||
|
|
0755319fac | ||
|
|
340b9cae84 | ||
|
|
4cd4bc9507 | ||
|
|
0e08664ef6 | ||
|
|
d7497dcd34 | ||
|
|
447133d5b2 | ||
|
|
b6f3640bbe | ||
|
|
d09bee3431 | ||
|
|
488157963a | ||
|
|
804bc533db | ||
|
|
65837610a1 | ||
|
|
dd7470922c | ||
|
|
0135a93a30 | ||
|
|
b12a8e5e75 | ||
|
|
ad0f83fd4b | ||
|
|
6f77fa5a9a | ||
|
|
331f531962 | ||
|
|
346399d35f | ||
|
|
33a08f00b3 | ||
|
|
2971029c42 | ||
|
|
3159fba65b | ||
|
|
b200b8bb5b | ||
|
|
126f7cc51c | ||
|
|
bcf34d6b1a | ||
|
|
022960ade7 | ||
|
|
b1e2e6462d | ||
|
|
ae8275d0a7 | ||
|
|
2857398e11 | ||
|
|
834c0de941 | ||
|
|
db4499d8c7 | ||
|
|
b3f99b2b6c | ||
|
|
5e5058ba0e | ||
|
|
42acce308f | ||
|
|
66a3dc2a3c | ||
|
|
910f65b6d4 | ||
|
|
114d5b99af | ||
|
|
ac2638f17d | ||
|
|
97b13a13c2 | ||
|
|
1f53bc1fb7 | ||
|
|
87477d4529 | ||
|
|
94f26b69ee | ||
|
|
56a499a59f | ||
|
|
6102b277c8 | ||
|
|
9ce9829f3b | ||
|
|
c987fa07e8 | ||
|
|
27a280bc03 | ||
|
|
e2dff8db09 | ||
|
|
7faaa9dc1f | ||
|
|
f617913ad9 | ||
|
|
8ccc9fb342 | ||
|
|
161de70576 | ||
|
|
82e1ff76a5 | ||
|
|
e7cfb06afa | ||
|
|
d9d9d226d3 | ||
|
|
bcd1872ffa | ||
|
|
0bd3a5957b | ||
|
|
a450dfebc7 | ||
|
|
5f90095470 | ||
|
|
eb865e137f | ||
|
|
b7819fda76 | ||
|
|
0e2883d0c6 | ||
|
|
a8322b5ed2 | ||
|
|
43dc34ee8f | ||
|
|
0300051e7c | ||
|
|
b8cb98510f | ||
|
|
20e0f6af45 | ||
|
|
50b061527e | ||
|
|
22b30cf92f | ||
|
|
e0c7a80fe0 | ||
|
|
87cfb2fb20 | ||
|
|
ebdce4c0ed | ||
|
|
4ebdb077b5 | ||
|
|
2eb14db055 | ||
|
|
776dec14b7 | ||
|
|
320b1dd5a9 | ||
|
|
a6ecd0496f | ||
|
|
7f3a003200 | ||
|
|
8b1f0ba0b9 | ||
|
|
84631f03c5 | ||
|
|
e506ba2326 | ||
|
|
c5171cf014 | ||
|
|
757b84f952 | ||
|
|
0e89f8af41 | ||
|
|
7403598df9 | ||
|
|
322d3a21b0 | ||
|
|
aa9a2142b8 | ||
|
|
ed64644d10 | ||
|
|
45d6132ffd | ||
|
|
b26e28e733 | ||
|
|
2b13af0a5e | ||
|
|
6cc6498844 | ||
|
|
313ceef1ed | ||
|
|
9e275e7ced | ||
|
|
3ee30cf4f1 | ||
|
|
0ea24b399d | ||
|
|
aea8e0a470 | ||
|
|
ba13d1fd79 | ||
|
|
8ec6f313a4 | ||
|
|
0b4b78ff21 | ||
|
|
52f0b47273 | ||
|
|
df4a121d77 | ||
|
|
f839142530 | ||
|
|
b58525263e | ||
|
|
46c99e382f | ||
|
|
268fee7f94 | ||
|
|
caffed92f4 | ||
|
|
2fd44174ce | ||
|
|
7119380716 | ||
|
|
5eed09a8fc | ||
|
|
3e253c2ee4 | ||
|
|
8d70da4d8e |
@@ -0,0 +1,61 @@
|
||||
# Правила
|
||||
|
||||
## ⛔ ОТВЕЧАТЬ КРАТКО — АБСОЛЮТНОЕ ПРАВИЛО
|
||||
- Вопрос → короткий ответ → СТОП.
|
||||
- Ничего лишнего.
|
||||
- Код — только по запросу.
|
||||
|
||||
## ⛔⛔⛔ ВОПРОС = СТОП
|
||||
|
||||
**Если в сообщении есть вопрос в ЛЮБОЙ форме** ("так ?", "верно ?", "почему ?", "как ?", "так же ?" и т.д.):
|
||||
1. ТОЛЬКО ответить на вопрос
|
||||
2. ОСТАНОВИТЬСЯ
|
||||
3. ЖДАТЬ следующей команды
|
||||
**ЗАПРЕЩЕНО** начинать работу, писать код, запускать команды — без явного "делай".
|
||||
|
||||
1. **⛔⛔⛔ АБСОЛЮТНЫЙ ЗАПРЕТ: не трогать и не читать рабочий код с целью подготовки к правке — без ПРЯМОГО указания "делай". Даже чтение файлов перед правкой — СТОП, сначала разрешение.**
|
||||
|
||||
2. Файлы редактируются локально:
|
||||
~/fission-src (текущая рабочая папка)
|
||||
|
||||
После ЛЮБЫХ изменений ОБЯЗАТЕЛЬНО синхронизировать на ВМ командой:
|
||||
rsync -az \
|
||||
-e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10" \
|
||||
~/fission-src/ \
|
||||
naeel@5.172.178.213:~/terra/fission-src/
|
||||
|
||||
|
||||
3. Git (add/commit/push) выполнять ЛОКАЛЬНО в ~/fission-src
|
||||
4. Docker, kubectl и другие инфраструктурные команды — только через SSH на ВМ:
|
||||
ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10 naeel@5.172.178.213 'КОМАНДА'
|
||||
|
||||
- не выполнять инфраструктурные команды локально
|
||||
- не открывать интерактивные сессии
|
||||
- не делать цепочки без необходимости
|
||||
|
||||
4. Перед запуском команд ОБЯЗАТЕЛЬНО убедиться, что синхронизация выполнена.
|
||||
|
||||
5. ЗАПРЕЩЕНО:
|
||||
- откатывать код
|
||||
- менять версии
|
||||
- ломать рабочее состояние
|
||||
|
||||
6. После каждого исправления:
|
||||
- git add/commit ЛОКАЛЬНО (в ~/fission-src)
|
||||
- затем синхронизация (rsync) на ВМ
|
||||
|
||||
## ⛔⛔⛔ ДЕЛАТЬ ТОЛЬКО ЧТО ПРЯМО ПРИКАЗАНО
|
||||
|
||||
**АБСОЛЮТНЫЙ ЗАПРЕТ на додумывание:**
|
||||
- Не расширять масштаб работы
|
||||
- Не выполнять "логичные следующие шаги"
|
||||
- Не инициировать дополнительные операции
|
||||
- Не делать ничего кроме того что сказано
|
||||
|
||||
**Пример (2026-05-01):**
|
||||
- Приказано: "собери"
|
||||
- Сделано: ✓ собрал образы v1.3.17 и v0.1.2
|
||||
- СТОП — жду команды дальше
|
||||
- **ЗАПРЕЩЕНО:** обновлять манифесты, заливать образы, применять на кластер, запускать тесты
|
||||
|
||||
**Исключение:** только если приказ явно включает цепочку ("собери И залей И тесты")
|
||||
@@ -20,6 +20,8 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
exclude-paths:
|
||||
- "test/**"
|
||||
groups:
|
||||
docker-images:
|
||||
patterns:
|
||||
@@ -30,7 +32,31 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
exclude-paths:
|
||||
- "test/**"
|
||||
groups:
|
||||
go-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: helm
|
||||
directory: /charts/fission-all
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
groups:
|
||||
helm-charts:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 5
|
||||
exclude-paths:
|
||||
- "test/**"
|
||||
groups:
|
||||
npm-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -0,0 +1,100 @@
|
||||
# Правила работы агента
|
||||
|
||||
## ⛔⛔⛔ DOCKER — ОБЯЗАТЕЛЬНЫЙ ПОРЯДОК ПЕРЕД КАЖДЫМ BUILD
|
||||
|
||||
1. УВЕЛИЧИТЬ ТЕГ в `console/deploy/console.yaml` (vX.Y.Z → vX.Y.Z+1)
|
||||
2. rsync на ВМ
|
||||
3. ПРОВЕРИТЬ что файлы на ВМ новые (grep ключевой строки)
|
||||
4. docker build с НОВЫМ тегом
|
||||
5. docker push с НОВЫМ тегом
|
||||
6. kubectl apply (не rollout restart — apply подтягивает новый тег)
|
||||
|
||||
**НИКОГДА не делать `docker build` со старым тегом — под не перетянет образ (imagePullPolicy: IfNotPresent)**
|
||||
|
||||
## Файловая система (актуально)
|
||||
|
||||
1. Все файлы редактируются локально: `~/fission-src` (текущая рабочая папка)
|
||||
2. После любых изменений — обязательно rsync на ВМ:
|
||||
rsync -az \
|
||||
-e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10" \
|
||||
~/fission-src/ \
|
||||
naeel@5.172.178.213:~/terra/fission-src/
|
||||
|
||||
3. Git (add/commit/push) выполнять ЛОКАЛЬНО в ~/fission-src
|
||||
4. Docker, kubectl и другие инфраструктурные команды — только через SSH на ВМ
|
||||
5. Перед запуском любой команды на ВМ обязательно убедиться, что синхронизация (rsync) выполнена
|
||||
6. SCP, sshfs, remote_dev и маунты больше НЕ используются
|
||||
7. Только rsync для синхронизации
|
||||
|
||||
Пример:
|
||||
1. Редактируешь локально (~/fission-src)
|
||||
2. rsync на ВМ
|
||||
3. Выполняешь команды через SSH на ВМ
|
||||
|
||||
## SSH
|
||||
|
||||
Все команды — только через SSH на ВМ. Локально — только читать и редактировать файлы.
|
||||
|
||||
```bash
|
||||
ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10 naeel@5.172.178.213 'КОМАНДА'
|
||||
```
|
||||
|
||||
Запрещено локально: `go`, `docker`, `kubectl`, `helm`, `terraform`, `curl/wget`, `git push/pull`, любые скрипты проекта.
|
||||
|
||||
## Документация
|
||||
|
||||
- `doc/thinking/` — лог рассуждений агента (обязательно)
|
||||
- `doc/progress.md` — трекер задач
|
||||
- Старые файлы `doc/` не перезаписывать — новое в новых файлах с датой
|
||||
|
||||
## Git
|
||||
|
||||
- Git — ТОЛЬКО ЛОКАЛЬНО в `~/fission-src`. НИКОГДА через SSH на VM.
|
||||
- Разрешены ТОЛЬКО две операции: `git commit` и `git push`.
|
||||
- ЗАПРЕЩЕНО: git pull, git fetch, git rebase, git merge, git reset, git stash, git checkout — что угодно кроме commit и push.
|
||||
- Если push отклонён — СТОП, доложить пользователю. Не лезть в pull/merge/rebase самостоятельно.
|
||||
|
||||
Версионирование тегами: `vMAJOR.MINOR.PATCH`
|
||||
- Patch — любое изменение кода
|
||||
- Minor — новая фича / компонент
|
||||
- Major — breaking change
|
||||
|
||||
```bash
|
||||
git tag vX.Y.Z && git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
## ⛔ ТЕРМИНАЛЬНЫЙ БУФЕР — НИКОГДА НЕ ЧИТАТЬ СТАРЫЙ
|
||||
|
||||
**АБСОЛЮТНОЕ ПРАВИЛО:**
|
||||
- get_terminal_output из старых сессий — МУСОР. Там старые прогоны.
|
||||
- Всегда запускать новую команду через SSH и читать её вывод напрямую.
|
||||
- НИКОГДА не читать буфер терминала из предыдущей сессии как актуальные данные.
|
||||
- Актуальный результат — только из команды, которая была запущена СЕЙЧАС.
|
||||
|
||||
## ⛔ ДОКУМЕНТАЦИЯ ТЕСТ-ПРОГОНОВ — В РЕАЛЬНОМ ВРЕМЕНИ
|
||||
|
||||
**Правила:**
|
||||
1. Перед запуском `run_all.sh` — создать файл `test-results/YYYY-MM-DD_HH-MM.log` и записать в него метку времени и что запускается.
|
||||
2. Запускать `run_all.sh 2>&1 | tee ~/terra/fission-src/test-results/YYYY-MM-DD_HH-MM.log` — вывод пишется сразу в файл и отображается в терминале.
|
||||
3. После завершения — rsync лога локально. Лог остаётся как документация.
|
||||
4. Папка `test-results/` в репозитории — `.gitignore` не добавлять, логи коммитить.
|
||||
|
||||
**Формат запуска:**
|
||||
```bash
|
||||
LOG="test-results/$(date +%Y-%m-%d_%H-%M).log"
|
||||
ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no naeel@5.172.178.213 \
|
||||
"bash ~/terra/fission-src/scripts/run_all.sh 2>&1 | tee ~/terra/fission-src/${LOG}"
|
||||
rsync -az -e "ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no" \
|
||||
naeel@5.172.178.213:~/terra/fission-src/test-results/ ~/fission-src/test-results/
|
||||
```
|
||||
|
||||
**Никогда не разбираться с результатами по памяти / буферу / чату. Только лог.**
|
||||
|
||||
## Поведение агента
|
||||
|
||||
- **⛔⛔⛔ АБСОЛЮТНЫЙ ЗАПРЕТ: не читать и не трогать код с целью подготовки к правке — без прямого "делай". Даже чтение файлов перед правкой — СТОП, сначала разрешение.**
|
||||
- Не трогать рабочий код без явного указания
|
||||
- Не делать НИЧЕГО сверх того, о чём явно приказали — ни git-команд, ни rebase, ни дополнительных шагов
|
||||
- Если для продолжения нужен выбор — СПРОСИТЬ разрешения, не делать самостоятельно
|
||||
- Деструктивные операции (`kubectl delete`, `rm -rf`, `terraform destroy` и др.) — только после явного подтверждения с указанием конкретных объектов
|
||||
- Отвечать кратко, без вступлений, извинений, благодарностей и прочей воды
|
||||
@@ -14,6 +14,10 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fission-codeql-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
permissions:
|
||||
@@ -24,23 +28,23 @@ jobs:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
|
||||
uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
|
||||
with:
|
||||
languages: go
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
|
||||
uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
|
||||
|
||||
@@ -12,16 +12,20 @@ on: [pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fission-dependency-review-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
|
||||
|
||||
@@ -21,22 +21,17 @@ jobs:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: Install dashboard linter
|
||||
run: |
|
||||
go get github.com/grafana/dashboard-linter
|
||||
go install github.com/grafana/dashboard-linter
|
||||
|
||||
- name: Run dashboard linter
|
||||
run: ./hack/lint-dashboards.sh
|
||||
|
||||
@@ -17,12 +17,16 @@ on:
|
||||
- go.sum
|
||||
|
||||
env:
|
||||
GOLANGCI_LINT_VERSION: v1.63.4
|
||||
GOLANGCI_LINT_VERSION: v2.6.2
|
||||
GOLANGCI_LINT_TIMEOUT: 5m
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fission-lint-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
permissions:
|
||||
@@ -32,15 +36,15 @@ jobs:
|
||||
# if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
@@ -51,7 +55,7 @@ jobs:
|
||||
go mod download
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||
with:
|
||||
skip-cache: true
|
||||
version: ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
@@ -72,7 +76,7 @@ jobs:
|
||||
run: ./hack/runtests.sh
|
||||
|
||||
- name: Upload Coverage report to CodeCov
|
||||
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
|
||||
@@ -22,13 +22,18 @@ on:
|
||||
- go.sum
|
||||
|
||||
env:
|
||||
HELM_VERSION: v3.16.4
|
||||
KIND_VERSION: v0.26.0
|
||||
HELM_VERSION: v4.0.1
|
||||
KIND_VERSION: v0.30.0
|
||||
KIND_CLUSTER_NAME: kind
|
||||
SKAFFOLD_VERSION: v2.17.0
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fission-ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Job to run change detection
|
||||
integration-test:
|
||||
@@ -37,36 +42,36 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
kindversion: ["v1.28.15", "v1.30.8", "v1.32.0"]
|
||||
kindversion: ["v1.28.15", "v1.32.8", "v1.34.0"]
|
||||
os: [ubuntu-24.04]
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
repository: fission/examples
|
||||
path: examples
|
||||
|
||||
- name: Helm installation
|
||||
uses: Azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
|
||||
uses: Azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
|
||||
with:
|
||||
version: ${{ env.HELM_VERSION }}
|
||||
|
||||
- name: Kind Cluster
|
||||
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
|
||||
uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
|
||||
with:
|
||||
node_image: kindest/node:${{ matrix.kindversion }}
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
@@ -87,12 +92,12 @@ jobs:
|
||||
|
||||
- name: Install Skaffold
|
||||
run: |
|
||||
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.13.2/skaffold-linux-amd64
|
||||
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/${{ env.SKAFFOLD_VERSION }}/skaffold-linux-amd64
|
||||
sudo install skaffold /usr/local/bin/
|
||||
skaffold version
|
||||
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
install-only: true
|
||||
version: "~> v2"
|
||||
@@ -153,7 +158,7 @@ jobs:
|
||||
- name: Archive fission dump
|
||||
timeout-minutes: 10
|
||||
if: ${{ failure() || cancelled() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: fission-dump-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: fission-dump/*.zip
|
||||
@@ -162,7 +167,7 @@ jobs:
|
||||
- name: Archive prometheus dump
|
||||
timeout-minutes: 10
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: prom-dump-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: /tmp/prometheus/*
|
||||
@@ -171,7 +176,7 @@ jobs:
|
||||
- name: Archive kind logs
|
||||
timeout-minutes: 10
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: kind-logs-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: kind-logs/*
|
||||
@@ -185,36 +190,36 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
kindversion: ["v1.19.16"]
|
||||
kindversion: ["v1.31.12"]
|
||||
os: [ubuntu-24.04]
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
repository: fission/examples
|
||||
path: examples
|
||||
|
||||
- name: Helm installation
|
||||
uses: Azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
|
||||
uses: Azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
|
||||
with:
|
||||
version: ${{ env.HELM_VERSION }}
|
||||
|
||||
- name: Kind Cluster
|
||||
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
|
||||
uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
|
||||
with:
|
||||
node_image: kindest/node:${{ matrix.kindversion }}
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
@@ -235,12 +240,12 @@ jobs:
|
||||
|
||||
- name: Install Skaffold
|
||||
run: |
|
||||
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v2.13.2/skaffold-linux-amd64
|
||||
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/${{ env.SKAFFOLD_VERSION }}/skaffold-linux-amd64
|
||||
sudo install skaffold /usr/local/bin/
|
||||
skaffold version
|
||||
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
install-only: true
|
||||
version: "~> v2"
|
||||
@@ -304,7 +309,7 @@ jobs:
|
||||
- name: Archive fission dump
|
||||
timeout-minutes: 10
|
||||
if: ${{ failure() || cancelled() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: fission-dump-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: fission-dump/*.zip
|
||||
@@ -313,7 +318,7 @@ jobs:
|
||||
- name: Archive prometheus dump
|
||||
timeout-minutes: 10
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: prom-dump-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: /tmp/prometheus/*
|
||||
@@ -322,7 +327,7 @@ jobs:
|
||||
- name: Archive kind logs
|
||||
timeout-minutes: 10
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: kind-logs-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: kind-logs/*
|
||||
|
||||
+48
-121
@@ -6,36 +6,36 @@ on:
|
||||
- v2.**
|
||||
|
||||
env:
|
||||
KIND_VERSION: v0.26.0
|
||||
KIND_VERSION: v0.30.0
|
||||
KIND_NODE_IMAGE_TAG: v1.28.15
|
||||
KIND_CLUSTER_NAME: kind
|
||||
COSIGN_VERSION: v2.4.1
|
||||
COSIGN_VERSION: v3.0.3
|
||||
|
||||
jobs:
|
||||
create-draft-release:
|
||||
name: Create Draft Release with Goreleaser
|
||||
outputs:
|
||||
hashes: ${{ steps.binary.outputs.hashes }}
|
||||
ghcr_images: ${{ steps.image.outputs.ghcr_images }}
|
||||
version: ${{ steps.get_version.outputs.VERSION }}
|
||||
permissions:
|
||||
contents: write # for goreleaser/goreleaser-action to create a GitHub release
|
||||
packages: write # for goreleaser/goreleaser-action to upload artifacts to GitHub Packages
|
||||
id-token: write # for cosign to sign the image and binary
|
||||
attestations: write # for goreleaser/goreleaser-action to upload attestations
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
@@ -45,13 +45,13 @@ jobs:
|
||||
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
install-only: true
|
||||
version: "~> v2"
|
||||
|
||||
- name: Kind Cluster
|
||||
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
|
||||
uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
|
||||
with:
|
||||
node_image: kindest/node:${{ env.KIND_NODE_IMAGE_TAG }}
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
@@ -59,24 +59,27 @@ jobs:
|
||||
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
|
||||
- name: Check cosign install!
|
||||
run: cosign version
|
||||
|
||||
- uses: anchore/sbom-action/download-syft@df80a981bc6edbc4e220a492d3cbe9f5547a6e75 #v0.17.9
|
||||
- uses: anchore/sbom-action/download-syft@43a17d6e7add2b5535efe4dcae9952337c479a93 #v0.20.11
|
||||
|
||||
- name: Generate yaml for manifest, Minikube and Openshift installation
|
||||
run: ${GITHUB_WORKSPACE}/hack/build-yaml.sh $VERSION
|
||||
@@ -86,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Run GoReleaser
|
||||
id: goreleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
version: "~> v2"
|
||||
args: release
|
||||
@@ -95,15 +98,12 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DOCKER_CLI_EXPERIMENTAL: "enabled"
|
||||
|
||||
- name: Generate binary hashes
|
||||
id: binary
|
||||
env:
|
||||
ARTIFACTS: "${{ steps.goreleaser.outputs.artifacts }}"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path')
|
||||
echo "hashes=$(cat $checksum_file | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||
# Attest binary artifacts
|
||||
# https://goreleaser.com/customization/attestations/
|
||||
- name: Attest binary artifacts
|
||||
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||
with:
|
||||
subject-checksums: ./dist/checksums.txt
|
||||
|
||||
- name: Image digest
|
||||
id: image
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
ARTIFACTS: "${{ steps.goreleaser.outputs.artifacts }}"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
image_and_digest=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Docker Manifest") | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {name} + {digest} | join("@") | sub("^sha256:";"")' | grep -v latest)
|
||||
image_and_digest=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Docker Image") | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {name} + {digest} | join("@") | sub("^sha256:";"")' | grep -v latest)
|
||||
ghcr_images=$(echo "${image_and_digest}" | grep ghcr.io | jq -R -s -c '
|
||||
split("\n")
|
||||
| map(select(. != ""))
|
||||
@@ -124,40 +124,8 @@ jobs:
|
||||
)')
|
||||
echo "ghcr_images=$ghcr_images" >> "$GITHUB_OUTPUT"
|
||||
|
||||
binary-provenance:
|
||||
name: Create Binary Provenance
|
||||
needs: [create-draft-release]
|
||||
permissions:
|
||||
actions: read # To read the workflow path.
|
||||
id-token: write # To sign the provenance.
|
||||
contents: write # To add assets to a release.
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0 # Do not use commit hash
|
||||
with:
|
||||
base64-subjects: "${{ needs.create-draft-release.outputs.hashes }}"
|
||||
provenance-name: "fission_${{ needs.create-draft-release.outputs.version }}.intoto.jsonl"
|
||||
upload-assets: true # upload to a new release
|
||||
draft-release: true # create a draft release
|
||||
|
||||
image-provenance-ghcr:
|
||||
name: Create Image Provenance
|
||||
needs: [create-draft-release]
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.create-draft-release.outputs.ghcr_images) }}
|
||||
permissions:
|
||||
actions: read
|
||||
id-token: write
|
||||
packages: write
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0 # Do not use commit hash
|
||||
with:
|
||||
image: ${{ fromJson(toJson(matrix)).image }}
|
||||
digest: ${{ fromJson(toJson(matrix)).checksum }}
|
||||
registry-username: ${{ github.actor }}
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
image-sbom-ghcr:
|
||||
name: Create SBOM for container images
|
||||
image-sbom-provenance-ghcr:
|
||||
name: Create SBOM & Provenance for container images
|
||||
# Goreleaser does not support generating SBOM for container images.
|
||||
needs: [create-draft-release]
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -168,82 +136,41 @@ jobs:
|
||||
actions: write
|
||||
id-token: write
|
||||
packages: write
|
||||
attestations: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run Trivy in fs mode to generate SBOM
|
||||
uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # v0.29.0
|
||||
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # v0.33.1
|
||||
with:
|
||||
scan-type: "fs"
|
||||
format: "spdx-json"
|
||||
output: "spdx.sbom.json"
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
output: "sbom.spdx.json"
|
||||
- name: Attest SBOM for image
|
||||
uses: actions/attest-sbom@4651f806c01d8637787e274ac3bdf724ef169f34 # v3.0.0
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
- name: Sign image and sbom
|
||||
env:
|
||||
IMAGE: ${{ fromJson(toJson(matrix)).image }}
|
||||
DIGEST: ${{ fromJson(toJson(matrix)).checksum }}
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cosign attach sbom --sbom spdx.sbom.json $IMAGE@$DIGEST
|
||||
cosign sign -a git_sha=$GITHUB_SHA --attachment sbom $IMAGE@$DIGEST --yes
|
||||
|
||||
binary-provenance-verification-with-slsa-verifier:
|
||||
name : Verify Binary Provenance
|
||||
needs: [create-draft-release, binary-provenance]
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write # To download the assets from draft release.
|
||||
steps:
|
||||
- name: Install the verifier
|
||||
uses: slsa-framework/slsa-verifier/actions/installer@3714a2a4684014deb874a0e737dffa0ee02dd647 # v2.6.0
|
||||
|
||||
- name: Download assets
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PROVENANCE: ${{ needs.binary-provenance.outputs.provenance-name }}
|
||||
VERSION: ${{ needs.create-draft-release.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "repo=$GITHUB_REPOSITORY"
|
||||
echo "ref=$VERSION"
|
||||
gh -R "$GITHUB_REPOSITORY" release download "$VERSION" -p "$PROVENANCE"
|
||||
|
||||
- name: Verify assets
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CHECKSUMS: ${{ needs.create-draft-release.outputs.hashes }}
|
||||
PROVENANCE: ${{ needs.binary-provenance.outputs.provenance-name }}
|
||||
VERSION: ${{ needs.create-draft-release.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "CHECKSUMS=$CHECKSUMS"
|
||||
echo "PROVENANCE=$PROVENANCE"
|
||||
checksums=$(echo "$CHECKSUMS" | base64 -d)
|
||||
while read -r line; do
|
||||
fn=$(echo $line | cut -d ' ' -f2)
|
||||
echo "Verifying $fn"
|
||||
gh -R "$GITHUB_REPOSITORY" release download "$VERSION" -p "$fn"
|
||||
slsa-verifier verify-artifact --provenance-path "$PROVENANCE" \
|
||||
--source-uri "github.com/$GITHUB_REPOSITORY" \
|
||||
--source-tag "$VERSION" \
|
||||
"$fn"
|
||||
done <<<"$checksums"
|
||||
sbom-path: sbom.spdx.json
|
||||
subject-name: ${{ fromJson(toJson(matrix)).image }}
|
||||
subject-digest: ${{ fromJson(toJson(matrix)).checksum }}
|
||||
push-to-registry: true
|
||||
- name: Attest provenance for image
|
||||
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
|
||||
with:
|
||||
subject-name: ${{ fromJson(toJson(matrix)).image }}
|
||||
subject-digest: ${{ fromJson(toJson(matrix)).checksum }}
|
||||
push-to-registry: true
|
||||
|
||||
image-provenance-verification-with-cosign:
|
||||
name: Verify Image Provenance
|
||||
needs: [create-draft-release, image-provenance-ghcr]
|
||||
needs: [create-draft-release, image-sbom-provenance-ghcr]
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.create-draft-release.outputs.ghcr_images) }}
|
||||
@@ -251,14 +178,14 @@ jobs:
|
||||
permissions: read-all
|
||||
steps:
|
||||
- name: Login
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
|
||||
@@ -269,7 +196,7 @@ jobs:
|
||||
run: |
|
||||
echo "Verifying $IMAGE@$DIGEST"
|
||||
cosign verify-attestation \
|
||||
--type slsaprovenance \
|
||||
--type https://slsa.dev/provenance/v1 \
|
||||
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
||||
--certificate-identity-regexp '^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v[0-9]+.[0-9]+.[0-9]+$' \
|
||||
$IMAGE@$DIGEST
|
||||
--certificate-identity-regexp '^https://github.com/fission/fission/.github/workflows/release.yaml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(?:-rc[0-9]+)?$' \
|
||||
$IMAGE@$DIGEST
|
||||
|
||||
@@ -32,17 +32,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v3.pre.node20
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v3.pre.node20
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -73,6 +73,6 @@ jobs:
|
||||
# Upload the results to GitHub's code scanning dashboard (optional).
|
||||
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
|
||||
uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -22,13 +22,17 @@ on:
|
||||
- go.sum
|
||||
|
||||
env:
|
||||
HELM_VERSION: v3.16.4
|
||||
KIND_VERSION: v0.26.0
|
||||
HELM_VERSION: v3.19.0
|
||||
KIND_VERSION: v0.30.0
|
||||
KIND_CLUSTER_NAME: kind
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fission-upgrade-test-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
upgrade-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -36,37 +40,37 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
kindversion: ["v1.28.15"]
|
||||
kindversion: ["v1.31.12"]
|
||||
os: [ubuntu-24.04]
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
|
||||
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout action sources
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
|
||||
- name: Setup Helm
|
||||
uses: Azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
|
||||
uses: Azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
|
||||
with:
|
||||
version: ${{ env.HELM_VERSION }}
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
|
||||
uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
|
||||
with:
|
||||
node_image: kindest/node:${{ matrix.kindversion }}
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
|
||||
|
||||
- name: Install GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
install-only: true
|
||||
version: "~> v2"
|
||||
@@ -114,7 +118,7 @@ jobs:
|
||||
|
||||
- name: Archive fission dump
|
||||
if: ${{ failure() || cancelled() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: fission-dump-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: fission-dump/*.zip
|
||||
@@ -122,7 +126,7 @@ jobs:
|
||||
|
||||
- name: Archive kind logs
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: kind-logs-${{ github.run_id }}-${{ matrix.kindversion }}
|
||||
path: kind-logs/*
|
||||
|
||||
@@ -20,6 +20,8 @@ environments/php7/vendor/
|
||||
*.tfstate
|
||||
*.backup
|
||||
|
||||
*.token
|
||||
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
|
||||
+43
-27
@@ -1,35 +1,51 @@
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
# Default linter
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
||||
# Additional linters
|
||||
- gofmt
|
||||
- goimports
|
||||
- misspell
|
||||
- nakedret
|
||||
- unconvert
|
||||
- promlinter
|
||||
# Enable in future
|
||||
# - bodyclose
|
||||
# - dogsled
|
||||
# - dupl
|
||||
# - gosec
|
||||
# - nilerr
|
||||
# - prealloc
|
||||
# - revive
|
||||
# - unparam
|
||||
# - wrapcheck
|
||||
# - gocritic
|
||||
linters-settings:
|
||||
errcheck:
|
||||
ignore: go.uber.org/zap:Sync
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local: github.com/fission/fission
|
||||
- unconvert
|
||||
- unused
|
||||
- staticcheck
|
||||
settings:
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- (*go.uber.org/zap.Logger).Sync
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
rules:
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "QF1008"
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "QF1001"
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "QF1003"
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- github.com/fission/fission
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
+84
-220
@@ -70,235 +70,100 @@ builds:
|
||||
id: reporter
|
||||
binary: reporter
|
||||
dir: ./cmd/reporter
|
||||
dockers:
|
||||
- &docker-amd64
|
||||
use: buildx
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
ids:
|
||||
- builder
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/builder:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/builder:{{ .Tag }}-amd64"
|
||||
dockers_v2:
|
||||
- id: builder
|
||||
tags:
|
||||
- latest
|
||||
- "{{ .Tag }}"
|
||||
images:
|
||||
- "{{ .Env.GHCR_REPO }}/builder"
|
||||
labels:
|
||||
org.opencontainers.image.description: "The builder assists in building the fission function source code for deployment."
|
||||
org.opencontainers.image.source: "{{.GitURL}}"
|
||||
org.opencontainers.image.created: "{{.Date}}"
|
||||
org.opencontainers.image.revision: "{{.FullCommit}}"
|
||||
org.opencontainers.image.version: "{{.Tag}}"
|
||||
org.opencontainers.image.authors: "The Fission Authors https://fission.io/"
|
||||
org.opencontainers.image.vendor: "Fission"
|
||||
org.opencontainers.image.url: "https://fission.io/"
|
||||
dockerfile: cmd/builder/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=The builder assists in building the fission function source code for deployment."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-amd64
|
||||
ids:
|
||||
- fetcher
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:{{ .Tag }}-amd64"
|
||||
- id: fetcher
|
||||
tags:
|
||||
- latest
|
||||
- "{{ .Tag }}"
|
||||
images:
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher"
|
||||
labels:
|
||||
org.opencontainers.image.description: "Fetcher is a lightweight component used by environment and builder pods. Fetcher helps in fetch and upload of source/deployment packages and specializing environments."
|
||||
org.opencontainers.image.source: "{{.GitURL}}"
|
||||
org.opencontainers.image.created: "{{.Date}}"
|
||||
org.opencontainers.image.revision: "{{.FullCommit}}"
|
||||
org.opencontainers.image.version: "{{.Tag}}"
|
||||
org.opencontainers.image.authors: "The Fission Authors https://fission.io/"
|
||||
org.opencontainers.image.vendor: "Fission"
|
||||
org.opencontainers.image.url: "https://fission.io/"
|
||||
dockerfile: cmd/fetcher/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=Fetcher is a lightweight component used by environment and builder pods. Fetcher helps in fetch and upload of source/deployment packages and specializing environments."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-amd64
|
||||
ids:
|
||||
- fission-bundle
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:{{ .Tag }}-amd64"
|
||||
- id: fission-bundle
|
||||
tags:
|
||||
- latest
|
||||
- "{{ .Tag }}"
|
||||
images:
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle"
|
||||
labels:
|
||||
org.opencontainers.image.description: "fission-bundle is a component which is a single binary for all components. Most server side components running on server side are fission-bundle binary wrapped in container and used with different arguments."
|
||||
org.opencontainers.image.source: "{{.GitURL}}"
|
||||
org.opencontainers.image.created: "{{.Date}}"
|
||||
org.opencontainers.image.revision: "{{.FullCommit}}"
|
||||
org.opencontainers.image.version: "{{.Tag}}"
|
||||
org.opencontainers.image.authors: "The Fission Authors https://fission.io/"
|
||||
org.opencontainers.image.vendor: "Fission"
|
||||
org.opencontainers.image.url: "https://fission.io/"
|
||||
dockerfile: cmd/fission-bundle/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=fission-bundle is a component which is a single binary for all components. Most server side components running on server side are fission-bundle binary wrapped in container and used with different arguments."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-amd64
|
||||
ids:
|
||||
- pre-upgrade-checks
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:{{ .Tag }}-amd64"
|
||||
- id: pre-upgrade-checks
|
||||
tags:
|
||||
- latest
|
||||
- "{{ .Tag }}"
|
||||
images:
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks"
|
||||
labels:
|
||||
org.opencontainers.image.description: "Preupgradechecks ensures that Fission is ready for the targeted version upgrade by performing checks beforehand."
|
||||
org.opencontainers.image.source: "{{.GitURL}}"
|
||||
org.opencontainers.image.created: "{{.Date}}"
|
||||
org.opencontainers.image.revision: "{{.FullCommit}}"
|
||||
org.opencontainers.image.version: "{{.Tag}}"
|
||||
org.opencontainers.image.authors: "The Fission Authors https://fission.io/"
|
||||
org.opencontainers.image.vendor: "Fission"
|
||||
org.opencontainers.image.url: "https://fission.io/"
|
||||
dockerfile: cmd/preupgradechecks/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=Preupgradechecks ensures that Fission is ready for the targeted version upgrade by performing checks beforehand."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-amd64
|
||||
ids:
|
||||
- reporter
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:{{ .Tag }}-amd64"
|
||||
- id: reporter
|
||||
tags:
|
||||
- latest
|
||||
- "{{ .Tag }}"
|
||||
images:
|
||||
- "{{ .Env.GHCR_REPO }}/reporter"
|
||||
labels:
|
||||
org.opencontainers.image.description: "The reporter gathers information that assists in improving fission."
|
||||
org.opencontainers.image.source: "{{.GitURL}}"
|
||||
org.opencontainers.image.created: "{{.Date}}"
|
||||
org.opencontainers.image.revision: "{{.FullCommit}}"
|
||||
org.opencontainers.image.version: "{{.Tag}}"
|
||||
org.opencontainers.image.authors: "The Fission Authors https://fission.io/"
|
||||
org.opencontainers.image.vendor: "Fission"
|
||||
org.opencontainers.image.url: "https://fission.io/"
|
||||
dockerfile: cmd/reporter/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=The reporter gathers information that assists in improving fission."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- &docker-arm64
|
||||
use: buildx
|
||||
goos: linux
|
||||
goarch: arm64
|
||||
ids:
|
||||
- builder
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/builder:latest-arm64"
|
||||
- "{{ .Env.GHCR_REPO }}/builder:{{ .Tag }}-arm64"
|
||||
dockerfile: cmd/builder/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=The builder assists in building the fission function source code for deployment."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-arm64
|
||||
ids:
|
||||
- fetcher
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:latest-arm64"
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:{{ .Tag }}-arm64"
|
||||
dockerfile: cmd/fetcher/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=Fetcher is a lightweight component used by environment and builder pods. Fetcher helps in fetch and upload of source/deployment packages and specializing environments."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-arm64
|
||||
ids:
|
||||
- fission-bundle
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:latest-arm64"
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:{{ .Tag }}-arm64"
|
||||
dockerfile: cmd/fission-bundle/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=fission-bundle is a component which is a single binary for all components. Most server side components running on server side are fission-bundle binary wrapped in container and used with different arguments."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-arm64
|
||||
ids:
|
||||
- pre-upgrade-checks
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:latest-arm64"
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:{{ .Tag }}-arm64"
|
||||
dockerfile: cmd/preupgradechecks/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=Preupgradechecks ensures that Fission is ready for the targeted version upgrade by performing checks beforehand."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
- <<: *docker-arm64
|
||||
ids:
|
||||
- reporter
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:latest-arm64"
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:{{ .Tag }}-arm64"
|
||||
dockerfile: cmd/reporter/Dockerfile
|
||||
build_flag_templates:
|
||||
- "--label=org.opencontainers.image.description=The reporter gathers information that assists in improving fission."
|
||||
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Tag}}"
|
||||
- "--label=org.opencontainers.image.authors=The Fission Authors https://fission.io/"
|
||||
- "--label=org.opencontainers.image.vendor=Fission"
|
||||
- "--label=org.opencontainers.image.url=https://fission.io/"
|
||||
docker_manifests:
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/builder:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/builder:{{ .Tag }}-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/builder:{{ .Tag }}-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/fetcher:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:{{ .Tag }}-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:{{ .Tag }}-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/fission-bundle:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:{{ .Tag }}-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:{{ .Tag }}-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:{{ .Tag }}-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:{{ .Tag }}-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/reporter:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:{{ .Tag }}-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:{{ .Tag }}-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/builder:latest"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/builder:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/builder:latest-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/fetcher:latest"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fetcher:latest-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/fission-bundle:latest"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/fission-bundle:latest-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:latest"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/pre-upgrade-checks:latest-arm64"
|
||||
- name_template: "{{ .Env.GHCR_REPO }}/reporter:latest"
|
||||
image_templates:
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:latest-amd64"
|
||||
- "{{ .Env.GHCR_REPO }}/reporter:latest-arm64"
|
||||
changelog:
|
||||
disable: true
|
||||
archives:
|
||||
- id: fission
|
||||
builds:
|
||||
ids:
|
||||
- fission-cli
|
||||
name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
|
||||
format: binary
|
||||
formats:
|
||||
- binary
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
algorithm: sha256
|
||||
docker_digest:
|
||||
name_template: "docker-digests.txt"
|
||||
|
||||
# signs the checksum file
|
||||
# https://goreleaser.com/customization/sign
|
||||
@@ -306,13 +171,12 @@ signs:
|
||||
- id: cosign-binary
|
||||
env:
|
||||
- COSIGN_EXPERIMENTAL=1
|
||||
certificate: "${artifact}.pem"
|
||||
signature: "${artifact}.sig.bundle"
|
||||
cmd: cosign
|
||||
artifacts: binary
|
||||
artifacts: all
|
||||
args:
|
||||
- sign-blob
|
||||
- "--output-signature=${signature}"
|
||||
- "--output-certificate=${certificate}"
|
||||
- "--bundle=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes" # needed for cosign 2.0.0+
|
||||
|
||||
|
||||
@@ -61,17 +61,17 @@ install-fission-cli:
|
||||
### Codegen
|
||||
codegen:
|
||||
@./hack/update-codegen.sh
|
||||
go run sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile="hack/boilerplate.txt" paths="./..."
|
||||
go tool controller-gen object:headerFile="hack/boilerplate.txt" paths="./..."
|
||||
|
||||
### CRDs
|
||||
generate-crds:
|
||||
go run sigs.k8s.io/controller-tools/cmd/controller-gen crd \
|
||||
go tool controller-gen crd \
|
||||
paths=./pkg/apis/core/v1 \
|
||||
output:crd:artifacts:config=crds/v1
|
||||
|
||||
### Webhook generation: it generates webhook configs with help of kubebuilder:webhook tag
|
||||
generate-webhooks:
|
||||
go run sigs.k8s.io/controller-tools/cmd/controller-gen webhook \
|
||||
go tool controller-gen webhook \
|
||||
paths=./pkg/webhook \
|
||||
output:dir=charts/fission-all/templates/webhook-server
|
||||
|
||||
@@ -98,7 +98,7 @@ generate-cli-docs:
|
||||
|
||||
generate-crd-ref-docs:
|
||||
# crd-ref-docs: https://github.com/elastic/crd-ref-docs
|
||||
go run github.com/elastic/crd-ref-docs --source-path=pkg/apis/core/v1 --config=tools/crd-ref-docs/config.yaml --renderer markdown
|
||||
go tool crd-ref-docs --source-path=pkg/apis/core/v1 --config=tools/crd-ref-docs/config.yaml --renderer markdown
|
||||
cp tools/crd-ref-docs/header.md crd_docs.md
|
||||
cat out.md >> crd_docs.md && rm out.md
|
||||
mv crd_docs.md ../fission.io/content/en/docs/reference/crd-reference.md
|
||||
@@ -112,6 +112,7 @@ skaffold-prebuild:
|
||||
@cp -v cmd/fission-bundle/Dockerfile dist/fission-bundle_linux_amd64_v1/Dockerfile
|
||||
@cp -v cmd/reporter/Dockerfile dist/reporter_linux_amd64_v1/Dockerfile
|
||||
@cp -v cmd/preupgradechecks/Dockerfile dist/pre-upgrade-checks_linux_amd64_v1/Dockerfile
|
||||
@find dist/ -name 'Dockerfile' -exec sed -i.bak 's|$$TARGETPLATFORM/||g' {} +; find dist/ -name 'Dockerfile.bak' -delete
|
||||
|
||||
skaffold-deploy: skaffold-prebuild
|
||||
skaffold run -p $(SKAFFOLD_PROFILE)
|
||||
@@ -122,10 +123,3 @@ release:
|
||||
@./hack/release.sh $(VERSION)
|
||||
@./hack/release-tag.sh $(VERSION)
|
||||
@./hack/changelog.sh
|
||||
|
||||
## Envtest
|
||||
install-envtest:
|
||||
go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
|
||||
|
||||
setup-envtest:
|
||||
setup-envtest -p path use 1.30.x
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
<a href="https://scorecard.dev/viewer/?uri=github.com/fission/fission">
|
||||
<image alt="OpenSSF Scorecard" src="https://api.scorecard.dev/projects/github.com/fission/fission/badge">
|
||||
</a>
|
||||
<a href="https://www.bestpractices.dev/projects/4986">
|
||||
<img src="https://www.bestpractices.dev/projects/4986/badge">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
--------------
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
COPY fission-bundle /
|
||||
ENTRYPOINT ["/fission-bundle"]
|
||||
Executable
BIN
Binary file not shown.
@@ -1,9 +1,9 @@
|
||||
apiVersion: v2
|
||||
name: fission-all
|
||||
version: v1.21.0
|
||||
appVersion: v1.21.0
|
||||
version: 1.22.0
|
||||
appVersion: v1.22.0
|
||||
description: Fission is a fast serverless framework for Kubernetes.
|
||||
kubeVersion: ">=1.27.0-0"
|
||||
kubeVersion: ">=1.28.0-0"
|
||||
home: https://fission.io/
|
||||
icon: https://fission.io/images/fission-logo-white.svg
|
||||
sources:
|
||||
@@ -21,7 +21,6 @@ maintainers:
|
||||
email: vishal@infracloud.io
|
||||
- name: Sanket Sudake
|
||||
email: sanket@infracloud.io
|
||||
engine: gotpl
|
||||
type: application
|
||||
annotations:
|
||||
artifacthub.io/signKey: |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.23+
|
||||
- Kubernetes 1.28+
|
||||
- Helm 3+
|
||||
|
||||
## Get Repo Info
|
||||
|
||||
@@ -12,6 +12,12 @@ spec:
|
||||
matchLabels:
|
||||
svc: storagesvc
|
||||
application: fission-storage
|
||||
strategy:
|
||||
type: {{ .Values.storagesvc.deploymentStrategy.type }}
|
||||
{{- if eq .Values.storagesvc.deploymentStrategy.type "RollingUpdate" }}
|
||||
rollingUpdate:
|
||||
{{- toYaml .Values.storagesvc.deploymentStrategy.rollingUpdate | nindent 6}}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@@ -113,7 +119,7 @@ spec:
|
||||
priorityClassName: {{ .Values.priorityClassName }}
|
||||
{{- end }}
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraCoreComponentPodConfig }}
|
||||
|
||||
@@ -25,13 +25,13 @@ image: fission/fission-bundle
|
||||
## It is also used by the chart to identify version of the few more images apart from fission-bundle.
|
||||
## Keep it empty for using latest tag.
|
||||
##
|
||||
imageTag: v1.21.0
|
||||
imageTag: v1.22.1
|
||||
|
||||
## pullPolicy represents the pull policy to use for images in the chart.
|
||||
##
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
## imageppullsecrets
|
||||
## imagepullsecrets
|
||||
imagePullSecrets: []
|
||||
|
||||
## priorityClassName represents the priority class name to use for Fission components.
|
||||
@@ -80,7 +80,7 @@ builderNamespace: ""
|
||||
functionNamespace: ""
|
||||
|
||||
## Fission will watch the following namespaces along with the `defaultNamespace` for fission custom resources.
|
||||
## additionalFissionNamespaces:
|
||||
## additionalFissionNamespaces:
|
||||
## - namespace1
|
||||
## - namespace2
|
||||
## - namespace3
|
||||
@@ -124,7 +124,7 @@ fetcher:
|
||||
## image represents the image of the fetcher component.
|
||||
image: fission/fetcher
|
||||
## imageTag represents the tag of the image of the fetcher component.
|
||||
imageTag: v1.21.0
|
||||
imageTag: v1.22.0
|
||||
|
||||
## Fetcher is only for to downloading or uploading archive.
|
||||
## Normally, you don't need to change the value here, unless necessary.
|
||||
@@ -163,7 +163,7 @@ executor:
|
||||
## This is applicable to Pool Manager executor type only.
|
||||
##
|
||||
podReadyTimeout: 300s
|
||||
|
||||
|
||||
## Pod resources as:
|
||||
## resources:
|
||||
## limits:
|
||||
@@ -336,7 +336,7 @@ router:
|
||||
runAsGroup: 10001
|
||||
|
||||
## The builder manager watches the package & environments CRD changes and manages the builds of function source code.
|
||||
##
|
||||
##
|
||||
buildermgr:
|
||||
## Pod resources as:
|
||||
## resources:
|
||||
@@ -362,7 +362,7 @@ buildermgr:
|
||||
runAsGroup: 10001
|
||||
|
||||
## webhook is the component that validates API calls.
|
||||
## It contains validation and mutation for functions, triggers, environments, Kubernetes event watches, etc.
|
||||
## It contains validation and mutation for functions, triggers, environments, Kubernetes event watches, etc.
|
||||
##
|
||||
webhook:
|
||||
## Pod resources as:
|
||||
@@ -439,6 +439,16 @@ storagesvc:
|
||||
##
|
||||
resources: {}
|
||||
|
||||
## Deployment strategy defaults to RollingUpdate but use Recreate if new pods fail to
|
||||
## attach to the volume until the old pod has released it.
|
||||
## deploymentStrategy:
|
||||
## type: Recreate
|
||||
deploymentStrategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
|
||||
## Archive pruner removes archives from storage which are not referenced by any package.
|
||||
archivePruner:
|
||||
enabled: true
|
||||
@@ -541,7 +551,7 @@ serviceMonitor:
|
||||
# key: "value"
|
||||
|
||||
# The following components expose Prometheus metrics and have podmonitors in this chart (disabled by default)
|
||||
#
|
||||
#
|
||||
podMonitor:
|
||||
enabled: false
|
||||
##namespace in which you want to deploy podmonitor
|
||||
@@ -577,7 +587,7 @@ persistence:
|
||||
# region: <awsRegion>
|
||||
## For Minio and other s3 compatible storage systems set endPoint property
|
||||
# endPoint: <s3StorageUrl>
|
||||
|
||||
|
||||
## A manually managed Persistent Volume Claim name
|
||||
## Requires persistence.enabled: true
|
||||
## If defined, PVC must be created manually before volume will be bound
|
||||
@@ -696,7 +706,7 @@ preUpgradeChecks:
|
||||
image: fission/pre-upgrade-checks
|
||||
## pre-install/pre-upgrade checks image version
|
||||
##
|
||||
imageTag: v1.21.0
|
||||
imageTag: v1.22.0
|
||||
|
||||
## Fission post-install/post-upgrade reporting live in this image
|
||||
##
|
||||
@@ -764,12 +774,12 @@ authentication:
|
||||
## jwtSigningKey is the signing key used for
|
||||
## signing the JWT token
|
||||
##
|
||||
jwtSigningKey:
|
||||
jwtSigningKey:
|
||||
## jwtExpiryTime is the JWT expiry time
|
||||
## in seconds
|
||||
## default '120'
|
||||
##
|
||||
jwtExpiryTime:
|
||||
jwtExpiryTime:
|
||||
## jwtIssuer is the issuer of JWT
|
||||
## default 'fission'
|
||||
##
|
||||
@@ -874,7 +884,7 @@ runtimePodSpec:
|
||||
## Setting it false by default so that integration tests pass
|
||||
##
|
||||
enabled: false
|
||||
|
||||
|
||||
## Checkout PodSpec in https://fission.io/docs/reference/crd-reference/#runtime
|
||||
##
|
||||
podSpec:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:5497b01f36ef14a5198c0165e50ae6a0006d0c7457d4566f1110257e1c0812ed
|
||||
COPY builder /builder
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
ARG TARGETPLATFORM
|
||||
COPY $TARGETPLATFORM/builder /builder
|
||||
ENTRYPOINT ["/builder"]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:5497b01f36ef14a5198c0165e50ae6a0006d0c7457d4566f1110257e1c0812ed
|
||||
COPY fetcher /
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
ARG TARGETPLATFORM
|
||||
COPY $TARGETPLATFORM/fetcher /
|
||||
ENTRYPOINT ["/fetcher"]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:5497b01f36ef14a5198c0165e50ae6a0006d0c7457d4566f1110257e1c0812ed
|
||||
COPY fission-bundle /
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
ARG TARGETPLATFORM
|
||||
COPY $TARGETPLATFORM/fission-bundle /
|
||||
ENTRYPOINT ["/fission-bundle"]
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
COPY fission-bundle /
|
||||
ENTRYPOINT ["/fission-bundle"]
|
||||
BIN
Binary file not shown.
+181
-175
@@ -21,9 +21,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
"go.uber.org/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||
cnwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
@@ -48,116 +46,35 @@ import (
|
||||
"github.com/fission/fission/pkg/webhook"
|
||||
)
|
||||
|
||||
// runWebhook starts admission webhook server
|
||||
func runWebhook(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, port int) error {
|
||||
return webhook.Start(ctx, clientGen, logger, cnwebhook.Options{
|
||||
Port: port,
|
||||
})
|
||||
// Command line arguments
|
||||
type CommandLineArgs struct {
|
||||
// Flags
|
||||
canaryConfig bool
|
||||
kubewatcher bool
|
||||
timer bool
|
||||
mqt bool
|
||||
mqt_keda bool
|
||||
builderMgr bool
|
||||
showVersion bool
|
||||
logger bool
|
||||
|
||||
// Port values
|
||||
webhookPort int
|
||||
routerPort int
|
||||
executorPort int
|
||||
storageServicePort int
|
||||
|
||||
// URL values
|
||||
executorUrl string
|
||||
routerUrl string
|
||||
storageSvcUrl string
|
||||
|
||||
// Other configurations
|
||||
storageType string
|
||||
}
|
||||
|
||||
func runCanaryConfigServer(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface) error {
|
||||
return canaryconfigmgr.StartCanaryServer(ctx, clientGen, logger, mgr, false)
|
||||
}
|
||||
|
||||
func runRouter(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, port int, executorUrl string) error {
|
||||
return router.Start(ctx, clientGen, logger, mgr, port, eclient.MakeClient(logger, executorUrl))
|
||||
}
|
||||
|
||||
func runExecutor(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, port int) error {
|
||||
return executor.StartExecutor(ctx, clientGen, logger, mgr, port)
|
||||
}
|
||||
|
||||
func runKubeWatcher(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, routerUrl string) error {
|
||||
return kubewatcher.Start(ctx, clientGen, logger, mgr, routerUrl)
|
||||
}
|
||||
|
||||
func runTimer(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, routerUrl string) error {
|
||||
return timer.Start(ctx, clientGen, logger, mgr, routerUrl)
|
||||
}
|
||||
|
||||
func runMessageQueueMgr(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, routerUrl string) error {
|
||||
return mqtrigger.Start(ctx, clientGen, logger, mgr, routerUrl)
|
||||
}
|
||||
|
||||
// KEDA based MessageQueue Trigger Manager
|
||||
func runMQManager(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, routerURL string) error {
|
||||
return mqt.StartScalerManager(ctx, clientGen, logger, mgr, routerURL)
|
||||
}
|
||||
|
||||
func runStorageSvc(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, port int, storage storagesvc.Storage) error {
|
||||
return storagesvc.Start(ctx, clientGen, logger, storage, mgr, port)
|
||||
}
|
||||
|
||||
func runBuilderMgr(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, storageSvcUrl string) error {
|
||||
return buildermgr.Start(ctx, clientGen, logger, mgr, storageSvcUrl)
|
||||
}
|
||||
|
||||
func runLogger(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger) error {
|
||||
return functionLogger.Start(ctx, clientGen, logger)
|
||||
}
|
||||
|
||||
func getPort(logger *zap.Logger, portArg interface{}) int {
|
||||
portArgStr := portArg.(string)
|
||||
port, err := strconv.Atoi(portArgStr)
|
||||
if err != nil {
|
||||
logger.Fatal("invalid port number", zap.Error(err), zap.String("port", portArgStr))
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
func getStringArgWithDefault(arg interface{}, defaultValue string) string {
|
||||
if arg != nil {
|
||||
return arg.(string)
|
||||
} else {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func getServiceName(arguments map[string]interface{}) string {
|
||||
serviceName := "Fission-Unknown"
|
||||
|
||||
if arguments["--routerPort"] != nil {
|
||||
serviceName = "Fission-Router"
|
||||
} else if arguments["--executorPort"] != nil {
|
||||
serviceName = "Fission-Executor"
|
||||
} else if arguments["--kubewatcher"] == true {
|
||||
serviceName = "Fission-KubeWatcher"
|
||||
} else if arguments["--timer"] == true {
|
||||
serviceName = "Fission-Timer"
|
||||
} else if arguments["--mqt"] == true {
|
||||
serviceName = "Fission-MessageQueueTrigger"
|
||||
} else if arguments["--builderMgr"] == true {
|
||||
serviceName = "Fission-BuilderMgr"
|
||||
} else if arguments["--storageServicePort"] != nil {
|
||||
serviceName = "Fission-StorageSvc"
|
||||
} else if arguments["--mqt_keda"] == true {
|
||||
serviceName = "Fission-Keda-MQTrigger"
|
||||
}
|
||||
|
||||
return serviceName
|
||||
}
|
||||
|
||||
func exitWithSync(logger *zap.Logger) {
|
||||
// Ignore error, safe to ignore as per https://github.com/uber-go/zap/issues/328
|
||||
_ = logger.Sync()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
mgr := manager.New()
|
||||
defer mgr.Wait()
|
||||
|
||||
var err error
|
||||
|
||||
// From https://github.com/containous/traefik/pull/1817/files
|
||||
// Tell glog to log into STDERR. Otherwise, we risk
|
||||
// certain kinds of API errors getting logged into a directory not
|
||||
// available in a `FROM scratch` Docker container, causing glog to abort
|
||||
// hard with an exit code > 0.
|
||||
// TODO: fix the lint error. Error checking here is causing all components to crash with error "logtostderr not found"
|
||||
flag.Set("logtostderr", "true") //nolint: errcheck
|
||||
|
||||
usage := `fission-bundle: Package of all fission microservices: router, executor.
|
||||
// Usage information
|
||||
const usageText string = `fission-bundle: Package of all fission microservices: router, executor.
|
||||
|
||||
Use it to start one or more of the fission servers:
|
||||
|
||||
@@ -197,142 +114,231 @@ Options:
|
||||
--storageServicePort=<port> Port that the storage service should listen on.
|
||||
--executorUrl=<url> Executor URL. Not required if --executorPort is specified.
|
||||
--routerUrl=<url> Router URL.
|
||||
--etcdUrl=<etcdUrl> Etcd URL.
|
||||
--storageSvcUrl=<url> StorageService URL.
|
||||
--filePath=<filePath> Directory to store functions in.
|
||||
--namespace=<namespace> Kubernetes namespace in which to run function containers. Defaults to 'fission-function'.
|
||||
--kubewatcher Start Kubernetes events watcher.
|
||||
--timer Start Timer.
|
||||
--mqt Start message queue trigger.
|
||||
--mqt_keda Start message queue trigger of kind KEDA
|
||||
--builderMgr Start builder manager.
|
||||
--version Print version information
|
||||
`
|
||||
logger := loggerfactory.GetLogger()
|
||||
defer exitWithSync(logger)
|
||||
--version Print version information`
|
||||
|
||||
ctx := signals.SetupSignalHandler()
|
||||
profile.ProfileIfEnabled(ctx, logger, mgr)
|
||||
func main() {
|
||||
mgr := manager.New()
|
||||
defer mgr.Wait()
|
||||
|
||||
version := fmt.Sprintf("Fission Bundle Version: %s", info.BuildInfo().String())
|
||||
arguments, err := docopt.ParseArgs(usage, nil, version)
|
||||
if err != nil {
|
||||
logger.Error("failed to parse arguments", zap.Error(err))
|
||||
return
|
||||
// Set up command line parsing
|
||||
args := setupCommandLineArgs()
|
||||
|
||||
// Handle version request specially - exit after printing
|
||||
if args.showVersion {
|
||||
fmt.Printf("Fission Bundle Version: %s\n", info.BuildInfo().String())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
shutdown, err := otel.InitProvider(ctx, logger, getServiceName(arguments))
|
||||
// Initialize logger
|
||||
logger := loggerfactory.GetLogger()
|
||||
defer func() {
|
||||
// Ignore error, safe to ignore as per https://github.com/uber-go/zap/issues/328
|
||||
_ = logger.Sync()
|
||||
}()
|
||||
|
||||
// Set up signal handling for graceful shutdown
|
||||
ctx := signals.SetupSignalHandler()
|
||||
|
||||
// Enable profiling if configured
|
||||
profile.ProfileIfEnabled(ctx, logger, mgr)
|
||||
|
||||
// Initialize OpenTelemetry
|
||||
serviceName := getServiceNameFromArgs(args)
|
||||
shutdown, err := otel.InitProvider(ctx, logger, serviceName)
|
||||
if err != nil {
|
||||
logger.Error("error initializing provider for OTLP", zap.Error(err), zap.Any("argument", arguments))
|
||||
logger.Error("error initializing provider for OTLP", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if shutdown != nil {
|
||||
defer shutdown(ctx)
|
||||
}
|
||||
|
||||
executorUrl := getStringArgWithDefault(arguments["--executorUrl"], "http://executor.fission")
|
||||
routerUrl := getStringArgWithDefault(arguments["--routerUrl"], "http://router.fission")
|
||||
storageSvcUrl := getStringArgWithDefault(arguments["--storageSvcUrl"], "http://storagesvc.fission")
|
||||
// Initialize client generator
|
||||
clientGen := crd.NewClientGenerator()
|
||||
|
||||
if arguments["--webhookPort"] != nil {
|
||||
port := getPort(logger, arguments["--webhookPort"])
|
||||
err = runWebhook(ctx, clientGen, logger, port)
|
||||
// Start the appropriate service based on command line arguments
|
||||
startRequestedService(ctx, args, clientGen, logger, mgr)
|
||||
|
||||
<-ctx.Done()
|
||||
logger.Error("exiting")
|
||||
}
|
||||
|
||||
// setupCommandLineArgs parses command line arguments and returns them
|
||||
func setupCommandLineArgs() *CommandLineArgs {
|
||||
args := &CommandLineArgs{}
|
||||
|
||||
// Override the default usage function
|
||||
flag.Usage = func() {
|
||||
fmt.Println(usageText)
|
||||
}
|
||||
|
||||
// Tell glog to log into STDERR
|
||||
flag.Set("logtostderr", "true") //nolint: errcheck
|
||||
|
||||
// Define flags
|
||||
flag.BoolVar(&args.canaryConfig, "canaryConfig", false, "Start canary config server")
|
||||
flag.BoolVar(&args.kubewatcher, "kubewatcher", false, "Start Kubernetes events watcher")
|
||||
flag.BoolVar(&args.timer, "timer", false, "Start Timer")
|
||||
flag.BoolVar(&args.mqt, "mqt", false, "Start message queue trigger")
|
||||
flag.BoolVar(&args.mqt_keda, "mqt_keda", false, "Start message queue trigger of kind KEDA")
|
||||
flag.BoolVar(&args.builderMgr, "builderMgr", false, "Start builder manager")
|
||||
flag.BoolVar(&args.showVersion, "version", false, "Print version information")
|
||||
flag.BoolVar(&args.logger, "logger", false, "Start logger")
|
||||
|
||||
// Port flags
|
||||
flag.IntVar(&args.webhookPort, "webhookPort", 0, "Port that the webhook should listen on")
|
||||
flag.IntVar(&args.routerPort, "routerPort", 0, "Port that the router should listen on")
|
||||
flag.IntVar(&args.executorPort, "executorPort", 0, "Port that the executor should listen on")
|
||||
flag.IntVar(&args.storageServicePort, "storageServicePort", 0, "Port that the storage service should listen on")
|
||||
|
||||
// URL flags
|
||||
flag.StringVar(&args.executorUrl, "executorUrl", "http://executor.fission", "Executor URL")
|
||||
flag.StringVar(&args.routerUrl, "routerUrl", "http://router.fission", "Router URL")
|
||||
flag.StringVar(&args.storageSvcUrl, "storageSvcUrl", "http://storagesvc.fission", "StorageService URL")
|
||||
|
||||
// Other configuration flags
|
||||
flag.StringVar(&args.storageType, "storageType", "", "Type of storage to use")
|
||||
|
||||
// Parse flags
|
||||
flag.Parse()
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
// getServiceNameFromArgs determines which service is being started based on command line args
|
||||
func getServiceNameFromArgs(args *CommandLineArgs) string {
|
||||
serviceName := "Fission-Unknown"
|
||||
|
||||
if args.routerPort != 0 {
|
||||
serviceName = "Fission-Router"
|
||||
} else if args.executorPort != 0 {
|
||||
serviceName = "Fission-Executor"
|
||||
} else if args.kubewatcher {
|
||||
serviceName = "Fission-KubeWatcher"
|
||||
} else if args.timer {
|
||||
serviceName = "Fission-Timer"
|
||||
} else if args.mqt {
|
||||
serviceName = "Fission-MessageQueueTrigger"
|
||||
} else if args.builderMgr {
|
||||
serviceName = "Fission-BuilderMgr"
|
||||
} else if args.storageServicePort != 0 {
|
||||
serviceName = "Fission-StorageSvc"
|
||||
} else if args.mqt_keda {
|
||||
serviceName = "Fission-Keda-MQTrigger"
|
||||
}
|
||||
|
||||
return serviceName
|
||||
}
|
||||
|
||||
// startRequestedService starts the service specified by command line arguments
|
||||
func startRequestedService(ctx context.Context, args *CommandLineArgs, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface) {
|
||||
var err error
|
||||
|
||||
// Start the requested service based on command line arguments
|
||||
if args.webhookPort != 0 {
|
||||
err = webhook.Start(ctx, clientGen, logger, cnwebhook.Options{
|
||||
Port: args.webhookPort,
|
||||
})
|
||||
logger.Error("webhook server exited:", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--canaryConfig"] == true {
|
||||
err := runCanaryConfigServer(ctx, clientGen, logger, mgr)
|
||||
if args.canaryConfig {
|
||||
err = canaryconfigmgr.StartCanaryServer(ctx, clientGen, logger, mgr, false)
|
||||
if err != nil {
|
||||
logger.Error("canary config server exited with error: ", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--routerPort"] != nil {
|
||||
port := getPort(logger, arguments["--routerPort"])
|
||||
err = runRouter(ctx, clientGen, logger, mgr, port, executorUrl)
|
||||
if args.routerPort != 0 {
|
||||
err = router.Start(ctx, clientGen, logger, mgr, args.routerPort, eclient.MakeClient(logger, args.executorUrl))
|
||||
if err != nil {
|
||||
logger.Error("router exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--executorPort"] != nil {
|
||||
port := getPort(logger, arguments["--executorPort"])
|
||||
err = runExecutor(ctx, clientGen, logger, mgr, port)
|
||||
if args.executorPort != 0 {
|
||||
err = executor.StartExecutor(ctx, clientGen, logger, mgr, args.executorPort)
|
||||
if err != nil {
|
||||
logger.Error("executor exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--kubewatcher"] == true {
|
||||
err = runKubeWatcher(ctx, clientGen, logger, mgr, routerUrl)
|
||||
if args.kubewatcher {
|
||||
err = kubewatcher.Start(ctx, clientGen, logger, mgr, args.routerUrl)
|
||||
if err != nil {
|
||||
logger.Error("kubewatcher exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--timer"] == true {
|
||||
err = runTimer(ctx, clientGen, logger, mgr, routerUrl)
|
||||
if args.timer {
|
||||
err = timer.Start(ctx, clientGen, logger, mgr, args.routerUrl)
|
||||
if err != nil {
|
||||
logger.Error("timer exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--mqt"] == true {
|
||||
err = runMessageQueueMgr(ctx, clientGen, logger, mgr, routerUrl)
|
||||
if args.mqt {
|
||||
err = mqtrigger.Start(ctx, clientGen, logger, mgr, args.routerUrl)
|
||||
if err != nil {
|
||||
logger.Error("message queue manager exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--mqt_keda"] == true {
|
||||
err = runMQManager(ctx, clientGen, logger, mgr, routerUrl)
|
||||
if args.mqt_keda {
|
||||
err = mqt.StartScalerManager(ctx, clientGen, logger, mgr, args.routerUrl)
|
||||
if err != nil {
|
||||
logger.Error("mqt scaler manager exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--builderMgr"] == true {
|
||||
err = runBuilderMgr(ctx, clientGen, logger, mgr, storageSvcUrl)
|
||||
if args.builderMgr {
|
||||
err = buildermgr.Start(ctx, clientGen, logger, mgr, args.storageSvcUrl)
|
||||
if err != nil {
|
||||
logger.Error("builder manager exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--logger"] == true {
|
||||
err = runLogger(ctx, clientGen, logger)
|
||||
if args.logger {
|
||||
err = functionLogger.Start(ctx, clientGen, logger)
|
||||
if err != nil {
|
||||
logger.Error("logger exited", zap.Error(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if arguments["--storageServicePort"] != nil {
|
||||
port := getPort(logger, arguments["--storageServicePort"])
|
||||
if args.storageServicePort != 0 {
|
||||
startStorageService(ctx, args, clientGen, logger, mgr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var storage storagesvc.Storage
|
||||
// startStorageService initializes and starts the storage service
|
||||
func startStorageService(ctx context.Context, args *CommandLineArgs, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface) {
|
||||
var storage storagesvc.Storage
|
||||
|
||||
if arguments["--storageType"] != nil && arguments["--storageType"] == string(storagesvc.StorageTypeS3) {
|
||||
storage = storagesvc.NewS3Storage()
|
||||
} else if arguments["--storageType"] == string(storagesvc.StorageTypeLocal) {
|
||||
storage = storagesvc.NewLocalStorage("/fission")
|
||||
}
|
||||
err := runStorageSvc(ctx, clientGen, logger, mgr, port, storage)
|
||||
if err != nil {
|
||||
logger.Error("storage service exited", zap.Error(err))
|
||||
return
|
||||
}
|
||||
if args.storageType == string(storagesvc.StorageTypeS3) {
|
||||
storage = storagesvc.NewS3Storage()
|
||||
} else if args.storageType == string(storagesvc.StorageTypeLocal) {
|
||||
storage = storagesvc.NewLocalStorage("/fission")
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
logger.Error("exiting")
|
||||
err := storagesvc.Start(ctx, clientGen, logger, storage, mgr, args.storageServicePort)
|
||||
if err != nil {
|
||||
logger.Error("storage service exited", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||
@@ -41,12 +40,12 @@ import (
|
||||
func Start(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface, routerUrl string) error {
|
||||
fissionClient, err := clientGen.GetFissionClient()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get fission client")
|
||||
return fmt.Errorf("failed to get fission client: %w", err)
|
||||
}
|
||||
|
||||
err = crd.WaitForFunctionCRDs(ctx, logger, fissionClient)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error waiting for CRDs")
|
||||
return fmt.Errorf("error waiting for CRDs: %w", err)
|
||||
}
|
||||
|
||||
mqType := (fv1.MessageQueueType)(os.Getenv("MESSAGE_QUEUE_TYPE"))
|
||||
|
||||
@@ -14,7 +14,8 @@ limitations under the License.
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
|
||||
@@ -65,7 +66,7 @@ func App(clientOptions cmd.ClientOptions) *cobra.Command {
|
||||
// }
|
||||
client, err := cmd.NewClient(clientOptions)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get fission client")
|
||||
return fmt.Errorf("failed to get fission client: %w", err)
|
||||
}
|
||||
cmd.SetClientset(*client)
|
||||
return nil
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:5497b01f36ef14a5198c0165e50ae6a0006d0c7457d4566f1110257e1c0812ed
|
||||
COPY pre-upgrade-checks /
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
ARG TARGETPLATFORM
|
||||
COPY $TARGETPLATFORM/pre-upgrade-checks /
|
||||
ENTRYPOINT ["/pre-upgrade-checks"]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -12,8 +11,7 @@ import (
|
||||
func TestPreUpgradeTaskClient(t *testing.T) {
|
||||
f := framework.NewFramework()
|
||||
defer f.Logger().Sync()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
err := f.Start(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:5497b01f36ef14a5198c0165e50ae6a0006d0c7457d4566f1110257e1c0812ed
|
||||
COPY reporter /
|
||||
FROM cgr.dev/chainguard/static:latest@sha256:a301031ffd4ed67f35ca7fa6cf3dad9937b5fa47d7493955a18d9b4ca5412d1a
|
||||
ARG TARGETPLATFORM
|
||||
COPY $TARGETPLATFORM/reporter /
|
||||
ENTRYPOINT ["/reporter"]
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: canaryconfigs.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
|
||||
+1122
-144
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: functions.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
@@ -93,7 +93,9 @@ spec:
|
||||
policies:
|
||||
description: |-
|
||||
policies is a list of potential scaling polices which can be used during scaling.
|
||||
At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
|
||||
If not set, use the default values:
|
||||
- For scale up: allow doubling the number of pods, or an absolute change of 4 pods in a 15s window.
|
||||
- For scale down: allow all pods to be removed in a 15s window.
|
||||
items:
|
||||
description: HPAScalingPolicy is a single policy
|
||||
which must hold true for a specified past interval.
|
||||
@@ -136,6 +138,24 @@ spec:
|
||||
- For scale down: 300 (i.e. the stabilization window is 300 seconds long).
|
||||
format: int32
|
||||
type: integer
|
||||
tolerance:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: |-
|
||||
tolerance is the tolerance on the ratio between the current and desired
|
||||
metric value under which no updates are made to the desired number of
|
||||
replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not
|
||||
set, the default cluster-wide tolerance is applied (by default 10%).
|
||||
|
||||
For example, if autoscaling is configured with a memory consumption target of 100Mi,
|
||||
and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be
|
||||
triggered when the actual consumption falls below 95Mi or exceeds 101Mi.
|
||||
|
||||
This is an alpha field and requires enabling the HPAConfigurableTolerance
|
||||
feature gate.
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
type: object
|
||||
scaleUp:
|
||||
description: |-
|
||||
@@ -148,7 +168,9 @@ spec:
|
||||
policies:
|
||||
description: |-
|
||||
policies is a list of potential scaling polices which can be used during scaling.
|
||||
At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
|
||||
If not set, use the default values:
|
||||
- For scale up: allow doubling the number of pods, or an absolute change of 4 pods in a 15s window.
|
||||
- For scale down: allow all pods to be removed in a 15s window.
|
||||
items:
|
||||
description: HPAScalingPolicy is a single policy
|
||||
which must hold true for a specified past interval.
|
||||
@@ -191,6 +213,24 @@ spec:
|
||||
- For scale down: 300 (i.e. the stabilization window is 300 seconds long).
|
||||
format: int32
|
||||
type: integer
|
||||
tolerance:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
description: |-
|
||||
tolerance is the tolerance on the ratio between the current and desired
|
||||
metric value under which no updates are made to the desired number of
|
||||
replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not
|
||||
set, the default cluster-wide tolerance is applied (by default 10%).
|
||||
|
||||
For example, if autoscaling is configured with a memory consumption target of 100Mi,
|
||||
and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be
|
||||
triggered when the actual consumption falls below 95Mi or exceeds 101Mi.
|
||||
|
||||
This is an alpha field and requires enabling the HPAConfigurableTolerance
|
||||
feature gate.
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
type: object
|
||||
type: object
|
||||
hpaMetrics:
|
||||
@@ -1047,7 +1087,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1062,7 +1101,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1229,7 +1267,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1244,7 +1281,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1338,8 +1374,8 @@ spec:
|
||||
most preferred is the one with the greatest sum of weights, i.e.
|
||||
for each node that meets all of the scheduling requirements (resource
|
||||
request, requiredDuringScheduling anti-affinity expressions, etc.),
|
||||
compute a sum by iterating through the elements of this field and adding
|
||||
"weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
|
||||
compute a sum by iterating through the elements of this field and subtracting
|
||||
"weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the
|
||||
node(s) with the highest sum are the most preferred.
|
||||
items:
|
||||
description: The weights of all of the matched WeightedPodAffinityTerm
|
||||
@@ -1409,7 +1445,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1424,7 +1459,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1591,7 +1625,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1606,7 +1639,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1739,8 +1771,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -1798,6 +1831,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -1858,14 +1928,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -1886,8 +1956,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -2151,6 +2222,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: |-
|
||||
@@ -2558,7 +2635,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -2613,10 +2690,10 @@ spec:
|
||||
restartPolicy:
|
||||
description: |-
|
||||
RestartPolicy defines the restart behavior of individual containers in a pod.
|
||||
This field may only be set for init containers, and the only allowed value is "Always".
|
||||
For non-init containers or when this field is not specified,
|
||||
This overrides the pod-level restart policy. When this field is not specified,
|
||||
the restart behavior is defined by the Pod's restart policy and the container type.
|
||||
Setting the RestartPolicy as "Always" for the init container will have the following effect:
|
||||
Additionally, setting the RestartPolicy as "Always" for the init container will
|
||||
have the following effect:
|
||||
this init container will be continually restarted on
|
||||
exit until all regular containers have terminated. Once all regular
|
||||
containers have completed, all init containers with restartPolicy "Always"
|
||||
@@ -2628,6 +2705,59 @@ spec:
|
||||
init container is started, or after any startupProbe has successfully
|
||||
completed.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. The rules are evaluated in
|
||||
order. Once a rule matches a container exit condition, the remaining
|
||||
rules are ignored. If no rule matches the container exit condition,
|
||||
the Container-level restart policy determines the whether the container
|
||||
is restarted or not. Constraints on the rules:
|
||||
- At most 20 rules are allowed.
|
||||
- Rules can have the same action.
|
||||
- Identical rules are not forbidden in validations.
|
||||
When rules are specified, container MUST set RestartPolicy explicitly
|
||||
even it if matches the Pod's RestartPolicy.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
SecurityContext defines the security options the container should be run with.
|
||||
@@ -3246,8 +3376,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -3305,6 +3436,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -3365,14 +3533,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -3393,8 +3561,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -3654,6 +3823,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: Probes are not allowed for ephemeral containers.
|
||||
@@ -4044,7 +4219,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -4100,9 +4275,53 @@ spec:
|
||||
description: |-
|
||||
Restart policy for the container to manage the restart behavior of each
|
||||
container within a pod.
|
||||
This may only be set for init containers. You cannot set this field on
|
||||
ephemeral containers.
|
||||
You cannot set this field on ephemeral containers.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. You cannot set this field on
|
||||
ephemeral containers.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
Optional: SecurityContext defines the security options the ephemeral container should be run with.
|
||||
@@ -4640,7 +4859,9 @@ spec:
|
||||
hostNetwork:
|
||||
description: |-
|
||||
Host networking requested for this pod. Use the host's network namespace.
|
||||
If this option is set, the ports that will be used must be specified.
|
||||
When using HostNetwork you should specify ports so the scheduler is aware.
|
||||
When `hostNetwork` is true, specified `hostPort` fields in port definitions must match `containerPort`,
|
||||
and unspecified `hostPort` fields in port definitions are defaulted to match `containerPort`.
|
||||
Default to false.
|
||||
type: boolean
|
||||
hostPID:
|
||||
@@ -4665,6 +4886,19 @@ spec:
|
||||
Specifies the hostname of the Pod
|
||||
If not specified, the pod's hostname will be set to a system-defined value.
|
||||
type: string
|
||||
hostnameOverride:
|
||||
description: |-
|
||||
HostnameOverride specifies an explicit override for the pod's hostname as perceived by the pod.
|
||||
This field only specifies the pod's hostname and does not affect its DNS records.
|
||||
When this field is set to a non-empty string:
|
||||
- It takes precedence over the values set in `hostname` and `subdomain`.
|
||||
- The Pod's hostname will be set to this value.
|
||||
- `setHostnameAsFQDN` must be nil or set to false.
|
||||
- `hostNetwork` must be set to false.
|
||||
|
||||
This field must be a valid DNS subdomain as defined in RFC 1123 and contain at most 64 characters.
|
||||
Requires the HostnameOverride feature gate to be enabled.
|
||||
type: string
|
||||
imagePullSecrets:
|
||||
description: |-
|
||||
ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
|
||||
@@ -4700,7 +4934,7 @@ spec:
|
||||
Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes.
|
||||
The resourceRequirements of an init container are taken into account during scheduling
|
||||
by finding the highest request/limit for each resource type, and then using the max of
|
||||
of that value or the sum of the normal containers. Limits are applied to init containers
|
||||
that value or the sum of the normal containers. Limits are applied to init containers
|
||||
in a similar fashion.
|
||||
Init containers cannot currently be added or removed.
|
||||
Cannot be updated.
|
||||
@@ -4746,8 +4980,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -4805,6 +5040,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -4865,14 +5137,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -4893,8 +5165,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -5158,6 +5431,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: |-
|
||||
@@ -5565,7 +5844,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -5620,10 +5899,10 @@ spec:
|
||||
restartPolicy:
|
||||
description: |-
|
||||
RestartPolicy defines the restart behavior of individual containers in a pod.
|
||||
This field may only be set for init containers, and the only allowed value is "Always".
|
||||
For non-init containers or when this field is not specified,
|
||||
This overrides the pod-level restart policy. When this field is not specified,
|
||||
the restart behavior is defined by the Pod's restart policy and the container type.
|
||||
Setting the RestartPolicy as "Always" for the init container will have the following effect:
|
||||
Additionally, setting the RestartPolicy as "Always" for the init container will
|
||||
have the following effect:
|
||||
this init container will be continually restarted on
|
||||
exit until all regular containers have terminated. Once all regular
|
||||
containers have completed, all init containers with restartPolicy "Always"
|
||||
@@ -5635,6 +5914,59 @@ spec:
|
||||
init container is started, or after any startupProbe has successfully
|
||||
completed.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. The rules are evaluated in
|
||||
order. Once a rule matches a container exit condition, the remaining
|
||||
rules are ignored. If no rule matches the container exit condition,
|
||||
the Container-level restart policy determines the whether the container
|
||||
is restarted or not. Constraints on the rules:
|
||||
- At most 20 rules are allowed.
|
||||
- Rules can have the same action.
|
||||
- Identical rules are not forbidden in validations.
|
||||
When rules are specified, container MUST set RestartPolicy explicitly
|
||||
even it if matches the Pod's RestartPolicy.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
SecurityContext defines the security options the container should be run with.
|
||||
@@ -6167,6 +6499,7 @@ spec:
|
||||
- spec.hostPID
|
||||
- spec.hostIPC
|
||||
- spec.hostUsers
|
||||
- spec.resources
|
||||
- spec.securityContext.appArmorProfile
|
||||
- spec.securityContext.seLinuxOptions
|
||||
- spec.securityContext.seccompProfile
|
||||
@@ -6320,7 +6653,7 @@ spec:
|
||||
description: |-
|
||||
Resources is the total amount of CPU and Memory resources required by all
|
||||
containers in the pod. It supports specifying Requests and Limits for
|
||||
"cpu" and "memory" resource names only. ResourceClaims are not supported.
|
||||
"cpu", "memory" and "hugepages-" resource names only. ResourceClaims are not supported.
|
||||
|
||||
This field enables fine-grained control over resource allocation for the
|
||||
entire pod, allowing resource sharing among containers in a pod.
|
||||
@@ -6333,7 +6666,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -6870,7 +7203,6 @@ spec:
|
||||
- Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Honor policy.
|
||||
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
type: string
|
||||
nodeTaintsPolicy:
|
||||
description: |-
|
||||
@@ -6881,7 +7213,6 @@ spec:
|
||||
- Ignore: node taints are ignored. All nodes are included.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Ignore policy.
|
||||
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
type: string
|
||||
topologyKey:
|
||||
description: |-
|
||||
@@ -7606,15 +7937,13 @@ spec:
|
||||
volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim.
|
||||
If specified, the CSI driver will create or update the volume with the attributes defined
|
||||
in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName,
|
||||
it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass
|
||||
will be applied to the claim but it's not allowed to reset this field to empty string once it is set.
|
||||
If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass
|
||||
will be set by the persistentvolume controller if it exists.
|
||||
it can be changed after the claim is created. An empty string or nil value indicates that no
|
||||
VolumeAttributesClass will be applied to the claim. If the claim enters an Infeasible error state,
|
||||
this field can be reset to its previous value (including nil) to cancel the modification.
|
||||
If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be
|
||||
set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource
|
||||
exists.
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/
|
||||
(Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default).
|
||||
type: string
|
||||
volumeMode:
|
||||
description: |-
|
||||
@@ -7796,12 +8125,10 @@ spec:
|
||||
description: |-
|
||||
glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime.
|
||||
Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/glusterfs/README.md
|
||||
properties:
|
||||
endpoints:
|
||||
description: |-
|
||||
endpoints is the endpoint name that details Glusterfs topology.
|
||||
More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod
|
||||
description: endpoints is the endpoint name that details
|
||||
Glusterfs topology.
|
||||
type: string
|
||||
path:
|
||||
description: |-
|
||||
@@ -7855,7 +8182,7 @@ spec:
|
||||
The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field.
|
||||
The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images.
|
||||
The volume will be mounted read-only (ro) and non-executable files (noexec).
|
||||
Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath).
|
||||
Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath) before 1.33.
|
||||
The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type.
|
||||
properties:
|
||||
pullPolicy:
|
||||
@@ -7880,7 +8207,7 @@ spec:
|
||||
description: |-
|
||||
iscsi represents an ISCSI Disk resource that is attached to a
|
||||
kubelet's host machine and then exposed to the pod.
|
||||
More info: https://examples.k8s.io/volumes/iscsi/README.md
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi
|
||||
properties:
|
||||
chapAuthDiscovery:
|
||||
description: chapAuthDiscovery defines whether support
|
||||
@@ -8302,6 +8629,111 @@ spec:
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
podCertificate:
|
||||
description: |-
|
||||
Projects an auto-rotating credential bundle (private key and certificate
|
||||
chain) that the pod can use either as a TLS client or server.
|
||||
|
||||
Kubelet generates a private key and uses it to send a
|
||||
PodCertificateRequest to the named signer. Once the signer approves the
|
||||
request and issues a certificate chain, Kubelet writes the key and
|
||||
certificate chain to the pod filesystem. The pod does not start until
|
||||
certificates have been issued for each podCertificate projected volume
|
||||
source in its spec.
|
||||
|
||||
Kubelet will begin trying to rotate the certificate at the time indicated
|
||||
by the signer using the PodCertificateRequest.Status.BeginRefreshAt
|
||||
timestamp.
|
||||
|
||||
Kubelet can write a single file, indicated by the credentialBundlePath
|
||||
field, or separate files, indicated by the keyPath and
|
||||
certificateChainPath fields.
|
||||
|
||||
The credential bundle is a single file in PEM format. The first PEM
|
||||
entry is the private key (in PKCS#8 format), and the remaining PEM
|
||||
entries are the certificate chain issued by the signer (typically,
|
||||
signers will return their certificate chain in leaf-to-root order).
|
||||
|
||||
Prefer using the credential bundle format, since your application code
|
||||
can read it atomically. If you use keyPath and certificateChainPath,
|
||||
your application must make two separate file reads. If these coincide
|
||||
with a certificate rotation, it is possible that the private key and leaf
|
||||
certificate you read may not correspond to each other. Your application
|
||||
will need to check for this condition, and re-read until they are
|
||||
consistent.
|
||||
|
||||
The named signer controls chooses the format of the certificate it
|
||||
issues; consult the signer implementation's documentation to learn how to
|
||||
use the certificates it issues.
|
||||
properties:
|
||||
certificateChainPath:
|
||||
description: |-
|
||||
Write the certificate chain at this path in the projected volume.
|
||||
|
||||
Most applications should use credentialBundlePath. When using keyPath
|
||||
and certificateChainPath, your application needs to check that the key
|
||||
and leaf certificate are consistent, because it is possible to read the
|
||||
files mid-rotation.
|
||||
type: string
|
||||
credentialBundlePath:
|
||||
description: |-
|
||||
Write the credential bundle at this path in the projected volume.
|
||||
|
||||
The credential bundle is a single file that contains multiple PEM blocks.
|
||||
The first PEM block is a PRIVATE KEY block, containing a PKCS#8 private
|
||||
key.
|
||||
|
||||
The remaining blocks are CERTIFICATE blocks, containing the issued
|
||||
certificate chain from the signer (leaf and any intermediates).
|
||||
|
||||
Using credentialBundlePath lets your Pod's application code make a single
|
||||
atomic read that retrieves a consistent key and certificate chain. If you
|
||||
project them to separate files, your application code will need to
|
||||
additionally check that the leaf certificate was issued to the key.
|
||||
type: string
|
||||
keyPath:
|
||||
description: |-
|
||||
Write the key at this path in the projected volume.
|
||||
|
||||
Most applications should use credentialBundlePath. When using keyPath
|
||||
and certificateChainPath, your application needs to check that the key
|
||||
and leaf certificate are consistent, because it is possible to read the
|
||||
files mid-rotation.
|
||||
type: string
|
||||
keyType:
|
||||
description: |-
|
||||
The type of keypair Kubelet will generate for the pod.
|
||||
|
||||
Valid values are "RSA3072", "RSA4096", "ECDSAP256", "ECDSAP384",
|
||||
"ECDSAP521", and "ED25519".
|
||||
type: string
|
||||
maxExpirationSeconds:
|
||||
description: |-
|
||||
maxExpirationSeconds is the maximum lifetime permitted for the
|
||||
certificate.
|
||||
|
||||
Kubelet copies this value verbatim into the PodCertificateRequests it
|
||||
generates for this projection.
|
||||
|
||||
If omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver
|
||||
will reject values shorter than 3600 (1 hour). The maximum allowable
|
||||
value is 7862400 (91 days).
|
||||
|
||||
The signer implementation is then free to issue a certificate with any
|
||||
lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600
|
||||
seconds (1 hour). This constraint is enforced by kube-apiserver.
|
||||
`kubernetes.io` signers will never issue certificates with a lifetime
|
||||
longer than 24 hours.
|
||||
format: int32
|
||||
type: integer
|
||||
signerName:
|
||||
description: Kubelet's generated CSRs will
|
||||
be addressed to this signer.
|
||||
type: string
|
||||
required:
|
||||
- keyType
|
||||
- signerName
|
||||
type: object
|
||||
secret:
|
||||
description: secret information about the secret
|
||||
data to project
|
||||
@@ -8436,7 +8868,6 @@ spec:
|
||||
description: |-
|
||||
rbd represents a Rados Block Device mount on the host that shares a pod's lifetime.
|
||||
Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/rbd/README.md
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
@@ -8742,7 +9173,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: httptriggers.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: kuberneteswatchtriggers.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: messagequeuetriggers.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
@@ -412,7 +412,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -427,7 +426,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -594,7 +592,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -609,7 +606,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -703,8 +699,8 @@ spec:
|
||||
most preferred is the one with the greatest sum of weights, i.e.
|
||||
for each node that meets all of the scheduling requirements (resource
|
||||
request, requiredDuringScheduling anti-affinity expressions, etc.),
|
||||
compute a sum by iterating through the elements of this field and adding
|
||||
"weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
|
||||
compute a sum by iterating through the elements of this field and subtracting
|
||||
"weight" from the sum if the node has pods which matches the corresponding podAffinityTerm; the
|
||||
node(s) with the highest sum are the most preferred.
|
||||
items:
|
||||
description: The weights of all of the matched WeightedPodAffinityTerm
|
||||
@@ -774,7 +770,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -789,7 +784,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -956,7 +950,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both matchLabelKeys and labelSelector.
|
||||
Also, matchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -971,7 +964,6 @@ spec:
|
||||
pod labels will be ignored. The default value is empty.
|
||||
The same key is forbidden to exist in both mismatchLabelKeys and labelSelector.
|
||||
Also, mismatchLabelKeys cannot be set when labelSelector isn't set.
|
||||
This is a beta field and requires enabling MatchLabelKeysInPodAffinity feature gate (enabled by default).
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -1104,8 +1096,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -1163,6 +1156,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -1223,14 +1253,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -1251,8 +1281,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -1516,6 +1547,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: |-
|
||||
@@ -1923,7 +1960,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -1978,10 +2015,10 @@ spec:
|
||||
restartPolicy:
|
||||
description: |-
|
||||
RestartPolicy defines the restart behavior of individual containers in a pod.
|
||||
This field may only be set for init containers, and the only allowed value is "Always".
|
||||
For non-init containers or when this field is not specified,
|
||||
This overrides the pod-level restart policy. When this field is not specified,
|
||||
the restart behavior is defined by the Pod's restart policy and the container type.
|
||||
Setting the RestartPolicy as "Always" for the init container will have the following effect:
|
||||
Additionally, setting the RestartPolicy as "Always" for the init container will
|
||||
have the following effect:
|
||||
this init container will be continually restarted on
|
||||
exit until all regular containers have terminated. Once all regular
|
||||
containers have completed, all init containers with restartPolicy "Always"
|
||||
@@ -1993,6 +2030,59 @@ spec:
|
||||
init container is started, or after any startupProbe has successfully
|
||||
completed.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. The rules are evaluated in
|
||||
order. Once a rule matches a container exit condition, the remaining
|
||||
rules are ignored. If no rule matches the container exit condition,
|
||||
the Container-level restart policy determines the whether the container
|
||||
is restarted or not. Constraints on the rules:
|
||||
- At most 20 rules are allowed.
|
||||
- Rules can have the same action.
|
||||
- Identical rules are not forbidden in validations.
|
||||
When rules are specified, container MUST set RestartPolicy explicitly
|
||||
even it if matches the Pod's RestartPolicy.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
SecurityContext defines the security options the container should be run with.
|
||||
@@ -2611,8 +2701,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -2670,6 +2761,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -2730,14 +2858,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -2758,8 +2886,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -3019,6 +3148,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: Probes are not allowed for ephemeral containers.
|
||||
@@ -3409,7 +3544,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -3465,9 +3600,53 @@ spec:
|
||||
description: |-
|
||||
Restart policy for the container to manage the restart behavior of each
|
||||
container within a pod.
|
||||
This may only be set for init containers. You cannot set this field on
|
||||
ephemeral containers.
|
||||
You cannot set this field on ephemeral containers.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. You cannot set this field on
|
||||
ephemeral containers.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
Optional: SecurityContext defines the security options the ephemeral container should be run with.
|
||||
@@ -4005,7 +4184,9 @@ spec:
|
||||
hostNetwork:
|
||||
description: |-
|
||||
Host networking requested for this pod. Use the host's network namespace.
|
||||
If this option is set, the ports that will be used must be specified.
|
||||
When using HostNetwork you should specify ports so the scheduler is aware.
|
||||
When `hostNetwork` is true, specified `hostPort` fields in port definitions must match `containerPort`,
|
||||
and unspecified `hostPort` fields in port definitions are defaulted to match `containerPort`.
|
||||
Default to false.
|
||||
type: boolean
|
||||
hostPID:
|
||||
@@ -4030,6 +4211,19 @@ spec:
|
||||
Specifies the hostname of the Pod
|
||||
If not specified, the pod's hostname will be set to a system-defined value.
|
||||
type: string
|
||||
hostnameOverride:
|
||||
description: |-
|
||||
HostnameOverride specifies an explicit override for the pod's hostname as perceived by the pod.
|
||||
This field only specifies the pod's hostname and does not affect its DNS records.
|
||||
When this field is set to a non-empty string:
|
||||
- It takes precedence over the values set in `hostname` and `subdomain`.
|
||||
- The Pod's hostname will be set to this value.
|
||||
- `setHostnameAsFQDN` must be nil or set to false.
|
||||
- `hostNetwork` must be set to false.
|
||||
|
||||
This field must be a valid DNS subdomain as defined in RFC 1123 and contain at most 64 characters.
|
||||
Requires the HostnameOverride feature gate to be enabled.
|
||||
type: string
|
||||
imagePullSecrets:
|
||||
description: |-
|
||||
ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
|
||||
@@ -4065,7 +4259,7 @@ spec:
|
||||
Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes.
|
||||
The resourceRequirements of an init container are taken into account during scheduling
|
||||
by finding the highest request/limit for each resource type, and then using the max of
|
||||
of that value or the sum of the normal containers. Limits are applied to init containers
|
||||
that value or the sum of the normal containers. Limits are applied to init containers
|
||||
in a similar fashion.
|
||||
Init containers cannot currently be added or removed.
|
||||
Cannot be updated.
|
||||
@@ -4111,8 +4305,9 @@ spec:
|
||||
present in a Container.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the environment variable. Must
|
||||
be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Name of the environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
value:
|
||||
description: |-
|
||||
@@ -4170,6 +4365,43 @@ spec:
|
||||
- fieldPath
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
fileKeyRef:
|
||||
description: |-
|
||||
FileKeyRef selects a key of the env file.
|
||||
Requires the EnvFiles feature gate to be enabled.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
The key within the env file. An invalid key will prevent the pod from starting.
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters.
|
||||
type: string
|
||||
optional:
|
||||
default: false
|
||||
description: |-
|
||||
Specify whether the file or its key must be defined. If the file or key
|
||||
does not exist, then the env var is not published.
|
||||
If optional is set to true and the specified key does not exist,
|
||||
the environment variable will not be set in the Pod's containers.
|
||||
|
||||
If optional is set to false and the specified key does not exist,
|
||||
an error will be returned during Pod creation.
|
||||
type: boolean
|
||||
path:
|
||||
description: |-
|
||||
The path within the volume from which to select the file.
|
||||
Must be relative and may not contain the '..' path or start with '..'.
|
||||
type: string
|
||||
volumeName:
|
||||
description: The name of the volume mount
|
||||
containing the env file.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- path
|
||||
- volumeName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
resourceFieldRef:
|
||||
description: |-
|
||||
Selects a resource of the container: only resources limits and requests
|
||||
@@ -4230,14 +4462,14 @@ spec:
|
||||
envFrom:
|
||||
description: |-
|
||||
List of sources to populate environment variables in the container.
|
||||
The keys defined within a source must be a C_IDENTIFIER. All invalid keys
|
||||
will be reported as an event when the container is starting. When a key exists in multiple
|
||||
The keys defined within a source may consist of any printable ASCII characters except '='.
|
||||
When a key exists in multiple
|
||||
sources, the value associated with the last source will take precedence.
|
||||
Values defined by an Env with a duplicate key will take precedence.
|
||||
Cannot be updated.
|
||||
items:
|
||||
description: EnvFromSource represents the source of a
|
||||
set of ConfigMaps
|
||||
set of ConfigMaps or Secrets
|
||||
properties:
|
||||
configMapRef:
|
||||
description: The ConfigMap to select from
|
||||
@@ -4258,8 +4490,9 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
prefix:
|
||||
description: An optional identifier to prepend to
|
||||
each key in the ConfigMap. Must be a C_IDENTIFIER.
|
||||
description: |-
|
||||
Optional text to prepend to the name of each environment variable.
|
||||
May consist of any printable ASCII characters except '='.
|
||||
type: string
|
||||
secretRef:
|
||||
description: The Secret to select from
|
||||
@@ -4523,6 +4756,12 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
type: object
|
||||
stopSignal:
|
||||
description: |-
|
||||
StopSignal defines which signal will be sent to a container when it is being stopped.
|
||||
If not specified, the default is defined by the container runtime in use.
|
||||
StopSignal can only be set for Pods with a non-empty .spec.os.name
|
||||
type: string
|
||||
type: object
|
||||
livenessProbe:
|
||||
description: |-
|
||||
@@ -4930,7 +5169,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -4985,10 +5224,10 @@ spec:
|
||||
restartPolicy:
|
||||
description: |-
|
||||
RestartPolicy defines the restart behavior of individual containers in a pod.
|
||||
This field may only be set for init containers, and the only allowed value is "Always".
|
||||
For non-init containers or when this field is not specified,
|
||||
This overrides the pod-level restart policy. When this field is not specified,
|
||||
the restart behavior is defined by the Pod's restart policy and the container type.
|
||||
Setting the RestartPolicy as "Always" for the init container will have the following effect:
|
||||
Additionally, setting the RestartPolicy as "Always" for the init container will
|
||||
have the following effect:
|
||||
this init container will be continually restarted on
|
||||
exit until all regular containers have terminated. Once all regular
|
||||
containers have completed, all init containers with restartPolicy "Always"
|
||||
@@ -5000,6 +5239,59 @@ spec:
|
||||
init container is started, or after any startupProbe has successfully
|
||||
completed.
|
||||
type: string
|
||||
restartPolicyRules:
|
||||
description: |-
|
||||
Represents a list of rules to be checked to determine if the
|
||||
container should be restarted on exit. The rules are evaluated in
|
||||
order. Once a rule matches a container exit condition, the remaining
|
||||
rules are ignored. If no rule matches the container exit condition,
|
||||
the Container-level restart policy determines the whether the container
|
||||
is restarted or not. Constraints on the rules:
|
||||
- At most 20 rules are allowed.
|
||||
- Rules can have the same action.
|
||||
- Identical rules are not forbidden in validations.
|
||||
When rules are specified, container MUST set RestartPolicy explicitly
|
||||
even it if matches the Pod's RestartPolicy.
|
||||
items:
|
||||
description: ContainerRestartRule describes how a container
|
||||
exit is handled.
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
Specifies the action taken on a container exit if the requirements
|
||||
are satisfied. The only possible value is "Restart" to restart the
|
||||
container.
|
||||
type: string
|
||||
exitCodes:
|
||||
description: Represents the exit codes to check on
|
||||
container exits.
|
||||
properties:
|
||||
operator:
|
||||
description: |-
|
||||
Represents the relationship between the container exit code(s) and the
|
||||
specified values. Possible values are:
|
||||
- In: the requirement is satisfied if the container exit code is in the
|
||||
set of specified values.
|
||||
- NotIn: the requirement is satisfied if the container exit code is
|
||||
not in the set of specified values.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
Specifies the set of values to check for container exit codes.
|
||||
At most 255 elements are allowed.
|
||||
items:
|
||||
format: int32
|
||||
type: integer
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
required:
|
||||
- operator
|
||||
type: object
|
||||
required:
|
||||
- action
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
securityContext:
|
||||
description: |-
|
||||
SecurityContext defines the security options the container should be run with.
|
||||
@@ -5532,6 +5824,7 @@ spec:
|
||||
- spec.hostPID
|
||||
- spec.hostIPC
|
||||
- spec.hostUsers
|
||||
- spec.resources
|
||||
- spec.securityContext.appArmorProfile
|
||||
- spec.securityContext.seLinuxOptions
|
||||
- spec.securityContext.seccompProfile
|
||||
@@ -5685,7 +5978,7 @@ spec:
|
||||
description: |-
|
||||
Resources is the total amount of CPU and Memory resources required by all
|
||||
containers in the pod. It supports specifying Requests and Limits for
|
||||
"cpu" and "memory" resource names only. ResourceClaims are not supported.
|
||||
"cpu", "memory" and "hugepages-" resource names only. ResourceClaims are not supported.
|
||||
|
||||
This field enables fine-grained control over resource allocation for the
|
||||
entire pod, allowing resource sharing among containers in a pod.
|
||||
@@ -5698,7 +5991,7 @@ spec:
|
||||
Claims lists the names of resources, defined in spec.resourceClaims,
|
||||
that are used by this container.
|
||||
|
||||
This is an alpha field and requires enabling the
|
||||
This field depends on the
|
||||
DynamicResourceAllocation feature gate.
|
||||
|
||||
This field is immutable. It can only be set for containers.
|
||||
@@ -6235,7 +6528,6 @@ spec:
|
||||
- Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Honor policy.
|
||||
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
type: string
|
||||
nodeTaintsPolicy:
|
||||
description: |-
|
||||
@@ -6246,7 +6538,6 @@ spec:
|
||||
- Ignore: node taints are ignored. All nodes are included.
|
||||
|
||||
If this value is nil, the behavior is equivalent to the Ignore policy.
|
||||
This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
|
||||
type: string
|
||||
topologyKey:
|
||||
description: |-
|
||||
@@ -6971,15 +7262,13 @@ spec:
|
||||
volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim.
|
||||
If specified, the CSI driver will create or update the volume with the attributes defined
|
||||
in the corresponding VolumeAttributesClass. This has a different purpose than storageClassName,
|
||||
it can be changed after the claim is created. An empty string value means that no VolumeAttributesClass
|
||||
will be applied to the claim but it's not allowed to reset this field to empty string once it is set.
|
||||
If unspecified and the PersistentVolumeClaim is unbound, the default VolumeAttributesClass
|
||||
will be set by the persistentvolume controller if it exists.
|
||||
it can be changed after the claim is created. An empty string or nil value indicates that no
|
||||
VolumeAttributesClass will be applied to the claim. If the claim enters an Infeasible error state,
|
||||
this field can be reset to its previous value (including nil) to cancel the modification.
|
||||
If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be
|
||||
set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource
|
||||
exists.
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/
|
||||
(Beta) Using this field requires the VolumeAttributesClass feature gate to be enabled (off by default).
|
||||
type: string
|
||||
volumeMode:
|
||||
description: |-
|
||||
@@ -7161,12 +7450,10 @@ spec:
|
||||
description: |-
|
||||
glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime.
|
||||
Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/glusterfs/README.md
|
||||
properties:
|
||||
endpoints:
|
||||
description: |-
|
||||
endpoints is the endpoint name that details Glusterfs topology.
|
||||
More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod
|
||||
description: endpoints is the endpoint name that details
|
||||
Glusterfs topology.
|
||||
type: string
|
||||
path:
|
||||
description: |-
|
||||
@@ -7220,7 +7507,7 @@ spec:
|
||||
The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field.
|
||||
The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images.
|
||||
The volume will be mounted read-only (ro) and non-executable files (noexec).
|
||||
Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath).
|
||||
Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath) before 1.33.
|
||||
The field spec.securityContext.fsGroupChangePolicy has no effect on this volume type.
|
||||
properties:
|
||||
pullPolicy:
|
||||
@@ -7245,7 +7532,7 @@ spec:
|
||||
description: |-
|
||||
iscsi represents an ISCSI Disk resource that is attached to a
|
||||
kubelet's host machine and then exposed to the pod.
|
||||
More info: https://examples.k8s.io/volumes/iscsi/README.md
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volumes/#iscsi
|
||||
properties:
|
||||
chapAuthDiscovery:
|
||||
description: chapAuthDiscovery defines whether support
|
||||
@@ -7667,6 +7954,111 @@ spec:
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
podCertificate:
|
||||
description: |-
|
||||
Projects an auto-rotating credential bundle (private key and certificate
|
||||
chain) that the pod can use either as a TLS client or server.
|
||||
|
||||
Kubelet generates a private key and uses it to send a
|
||||
PodCertificateRequest to the named signer. Once the signer approves the
|
||||
request and issues a certificate chain, Kubelet writes the key and
|
||||
certificate chain to the pod filesystem. The pod does not start until
|
||||
certificates have been issued for each podCertificate projected volume
|
||||
source in its spec.
|
||||
|
||||
Kubelet will begin trying to rotate the certificate at the time indicated
|
||||
by the signer using the PodCertificateRequest.Status.BeginRefreshAt
|
||||
timestamp.
|
||||
|
||||
Kubelet can write a single file, indicated by the credentialBundlePath
|
||||
field, or separate files, indicated by the keyPath and
|
||||
certificateChainPath fields.
|
||||
|
||||
The credential bundle is a single file in PEM format. The first PEM
|
||||
entry is the private key (in PKCS#8 format), and the remaining PEM
|
||||
entries are the certificate chain issued by the signer (typically,
|
||||
signers will return their certificate chain in leaf-to-root order).
|
||||
|
||||
Prefer using the credential bundle format, since your application code
|
||||
can read it atomically. If you use keyPath and certificateChainPath,
|
||||
your application must make two separate file reads. If these coincide
|
||||
with a certificate rotation, it is possible that the private key and leaf
|
||||
certificate you read may not correspond to each other. Your application
|
||||
will need to check for this condition, and re-read until they are
|
||||
consistent.
|
||||
|
||||
The named signer controls chooses the format of the certificate it
|
||||
issues; consult the signer implementation's documentation to learn how to
|
||||
use the certificates it issues.
|
||||
properties:
|
||||
certificateChainPath:
|
||||
description: |-
|
||||
Write the certificate chain at this path in the projected volume.
|
||||
|
||||
Most applications should use credentialBundlePath. When using keyPath
|
||||
and certificateChainPath, your application needs to check that the key
|
||||
and leaf certificate are consistent, because it is possible to read the
|
||||
files mid-rotation.
|
||||
type: string
|
||||
credentialBundlePath:
|
||||
description: |-
|
||||
Write the credential bundle at this path in the projected volume.
|
||||
|
||||
The credential bundle is a single file that contains multiple PEM blocks.
|
||||
The first PEM block is a PRIVATE KEY block, containing a PKCS#8 private
|
||||
key.
|
||||
|
||||
The remaining blocks are CERTIFICATE blocks, containing the issued
|
||||
certificate chain from the signer (leaf and any intermediates).
|
||||
|
||||
Using credentialBundlePath lets your Pod's application code make a single
|
||||
atomic read that retrieves a consistent key and certificate chain. If you
|
||||
project them to separate files, your application code will need to
|
||||
additionally check that the leaf certificate was issued to the key.
|
||||
type: string
|
||||
keyPath:
|
||||
description: |-
|
||||
Write the key at this path in the projected volume.
|
||||
|
||||
Most applications should use credentialBundlePath. When using keyPath
|
||||
and certificateChainPath, your application needs to check that the key
|
||||
and leaf certificate are consistent, because it is possible to read the
|
||||
files mid-rotation.
|
||||
type: string
|
||||
keyType:
|
||||
description: |-
|
||||
The type of keypair Kubelet will generate for the pod.
|
||||
|
||||
Valid values are "RSA3072", "RSA4096", "ECDSAP256", "ECDSAP384",
|
||||
"ECDSAP521", and "ED25519".
|
||||
type: string
|
||||
maxExpirationSeconds:
|
||||
description: |-
|
||||
maxExpirationSeconds is the maximum lifetime permitted for the
|
||||
certificate.
|
||||
|
||||
Kubelet copies this value verbatim into the PodCertificateRequests it
|
||||
generates for this projection.
|
||||
|
||||
If omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver
|
||||
will reject values shorter than 3600 (1 hour). The maximum allowable
|
||||
value is 7862400 (91 days).
|
||||
|
||||
The signer implementation is then free to issue a certificate with any
|
||||
lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600
|
||||
seconds (1 hour). This constraint is enforced by kube-apiserver.
|
||||
`kubernetes.io` signers will never issue certificates with a lifetime
|
||||
longer than 24 hours.
|
||||
format: int32
|
||||
type: integer
|
||||
signerName:
|
||||
description: Kubelet's generated CSRs will
|
||||
be addressed to this signer.
|
||||
type: string
|
||||
required:
|
||||
- keyType
|
||||
- signerName
|
||||
type: object
|
||||
secret:
|
||||
description: secret information about the secret
|
||||
data to project
|
||||
@@ -7801,7 +8193,6 @@ spec:
|
||||
description: |-
|
||||
rbd represents a Rados Block Device mount on the host that shares a pod's lifetime.
|
||||
Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/rbd/README.md
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: packages.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.1
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: timetriggers.fission.io
|
||||
spec:
|
||||
group: fission.io
|
||||
|
||||
+12
-10
@@ -1,13 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
commonLabels:
|
||||
group: fission.io
|
||||
resources:
|
||||
- fission.io_canaryconfigs.yaml
|
||||
- fission.io_environments.yaml
|
||||
- fission.io_functions.yaml
|
||||
- fission.io_httptriggers.yaml
|
||||
- fission.io_kuberneteswatchtriggers.yaml
|
||||
- fission.io_messagequeuetriggers.yaml
|
||||
- fission.io_packages.yaml
|
||||
- fission.io_timetriggers.yaml
|
||||
- fission.io_canaryconfigs.yaml
|
||||
- fission.io_environments.yaml
|
||||
- fission.io_functions.yaml
|
||||
- fission.io_httptriggers.yaml
|
||||
- fission.io_kuberneteswatchtriggers.yaml
|
||||
- fission.io_messagequeuetriggers.yaml
|
||||
- fission.io_packages.yaml
|
||||
- fission.io_timetriggers.yaml
|
||||
labels:
|
||||
- includeSelectors: true
|
||||
pairs:
|
||||
group: fission.io
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# deploy/multitenant/rbac.yaml
|
||||
#
|
||||
# RBAC required for the Fission multi-tenant NSWatcher components.
|
||||
#
|
||||
# Both fission-executor and fission-router must be allowed to list and watch
|
||||
# Namespaces at the cluster scope so that their NSWatchers can detect newly-
|
||||
# labeled Namespaces.
|
||||
#
|
||||
# The executor also needs additional write permissions to provision the
|
||||
# fission-fetcher ServiceAccount/Role/RoleBinding in new namespaces.
|
||||
# Apply once per cluster after installing Fission:
|
||||
#
|
||||
# kubectl apply -f deploy/multitenant/rbac.yaml
|
||||
#
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: fission-executor-ns-watcher
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: executor
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: fission-executor-ns-watcher
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: executor
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: fission-executor-ns-watcher
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-executor
|
||||
namespace: fission
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: fission-router-ns-watcher
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: router
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: fission-router-ns-watcher
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: router
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: fission-router-ns-watcher
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-router
|
||||
namespace: fission
|
||||
---
|
||||
# ClusterRole: allows fission-executor to create/update fission-fetcher SA,
|
||||
# Role and RoleBinding in any user namespace managed by NSWatcher.
|
||||
#
|
||||
# It also needs two less-obvious permissions:
|
||||
# 1. localsubjectaccessreviews.create — setupSAAndRoleBindings checks whether
|
||||
# the target SA already has each permission before creating missing rules.
|
||||
# 2. events.create — Kubernetes forbids creating a Role that grants permissions
|
||||
# the caller does not currently hold. Since fission-fetcher gets events.create,
|
||||
# fission-executor must hold it too in order to create that Role.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: fission-executor-sa-provisioner
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: executor
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["serviceaccounts"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create"]
|
||||
- apiGroups: ["authorization.k8s.io"]
|
||||
resources: ["localsubjectaccessreviews"]
|
||||
verbs: ["create"]
|
||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||
resources: ["roles", "rolebindings"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: fission-executor-sa-provisioner
|
||||
labels:
|
||||
app.kubernetes.io/name: fission
|
||||
app.kubernetes.io/component: executor
|
||||
app.kubernetes.io/part-of: fission-multitenant
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: fission-executor-sa-provisioner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: fission-executor
|
||||
namespace: fission
|
||||
@@ -0,0 +1,567 @@
|
||||
# Namespace Lifecycle Hardening — Implementation Notes
|
||||
## Дата: 2026-05-18
|
||||
## Ветка: `fix/namespace-lifecycle-hardening`
|
||||
## Коммиты: `3b93c5dc` (предыдущая сессия) → `4eedf95f` (эта сессия)
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст: что было сделано до этой сессии
|
||||
|
||||
### Предыдущие правки (коммит `3b93c5dc`)
|
||||
1. **`RemoveNamespace(ns string) bool`** — добавлен в `NamespaceResolver` (`pkg/utils/namespace.go`).
|
||||
`HandleWatcherNamespaceRemoval` теперь вызывает его при любой стратегии, очищая глобальный resolver. Это исправляет дедупликацию router/buildermgr при re-add NS.
|
||||
|
||||
2. **Параллельный `dispatch()`** — `pkg/utils/namespace_manager.go`: заменён последовательный обход подписчиков на параллельный с `sync.WaitGroup`. Исправляет 30-минутное окно, когда HTTPTrigger не видел namespace из-за того что обход был последовательным.
|
||||
|
||||
3. **`RunReconciler()`** — добавлен в `NamespaceManager` interface и реализован в `inMemoryNamespaceManager`. Каждые 30 секунд сканирует namespace-ы в фазе `NamespacePhaseFailed` и вызывает `DispatchResync`. Исправляет постоянно stuck-failed namespace при транзиентных k8s API ошибках.
|
||||
|
||||
### Что оставалось нерешённым (из аудита)
|
||||
Три проблемы, зафиксированные в `FORENSIC_ARCHITECTURE_AUDIT.md`:
|
||||
|
||||
**Проблема 1 — Executor dedup gap (Critical)**
|
||||
При удалении NS и повторном добавлении executor молча пропускал его.
|
||||
Причина: `gpm.poolPodC.envLister[ns]` и `deploy.deplLister[ns]` проверялись как дедупликация в `AddNamespace`, но никогда не очищались при удалении NS.
|
||||
Результат: повторно добавленный namespace не получал informers в executor → функции не запускались.
|
||||
|
||||
**Проблема 2 — Goroutine/FD leak (High)**
|
||||
При удалении NS старые informer factories продолжали работать (goroutines, file descriptors, LIST-запросы к k8s API каждые 30 минут).
|
||||
Причина: informers запускались с `ctx.Done()` родительского контекста всего процесса, без механизма per-NS остановки.
|
||||
|
||||
**Проблема 3 — Router stale routes (Medium)**
|
||||
После удаления NS router продолжал держать HTTPTrigger routes для этого namespace.
|
||||
Причина: `triggerInformer[ns]` и `funcInformer[ns]` не чистились, `syncTriggers()` не вызывался.
|
||||
|
||||
---
|
||||
|
||||
## 2. Анализ перед реализацией
|
||||
|
||||
### 2.1 Чтение интерфейса ExecutorType
|
||||
Файл: `pkg/executor/executortype/executortype.go`
|
||||
|
||||
```go
|
||||
// До правки — нет RemoveNamespace
|
||||
AddNamespace(ctx context.Context, ns string, mgr manager.Interface) error
|
||||
}
|
||||
```
|
||||
|
||||
Подтверждено: ни `grep`, ни LSP не нашли `RemoveNamespace` в executor types.
|
||||
|
||||
### 2.2 Анализ механизма дедупликации по каждому executor type
|
||||
|
||||
**poolmgr** (`gpm.go` строка 807):
|
||||
```go
|
||||
if _, ok := gpm.poolPodC.envLister[ns]; ok {
|
||||
return nil // already registered
|
||||
}
|
||||
```
|
||||
Деdup через `PoolPodController.envLister[ns]` — локальная карта, не связана с глобальным resolver.
|
||||
|
||||
**newdeploy** (`newdeploymgr.go` строка 917):
|
||||
```go
|
||||
if _, ok := deploy.deplLister[ns]; ok {
|
||||
return nil // already registered
|
||||
}
|
||||
```
|
||||
Деdup через `deploy.deplLister[ns]`.
|
||||
|
||||
**container** (`containermgr.go` строка 805):
|
||||
```go
|
||||
if _, ok := caaf.deplLister[ns]; ok {
|
||||
return nil // already registered
|
||||
}
|
||||
```
|
||||
Деdup через `caaf.deplLister[ns]`.
|
||||
|
||||
**router** (`httpTriggers.go` строка 461):
|
||||
```go
|
||||
if !utils.DefaultNSResolver().AddNamespace(ns) {
|
||||
return nil // already registered
|
||||
}
|
||||
```
|
||||
Деdup через глобальный resolver — **уже починен** предыдущим коммитом (`RemoveNamespace` в resolver).
|
||||
|
||||
**buildermgr envwatcher** (`envwatcher.go` строка 506):
|
||||
```go
|
||||
if _, exists := envw.envWatchInformer[ns]; exists {
|
||||
return
|
||||
}
|
||||
```
|
||||
Деdup через `envw.envWatchInformer[ns]`.
|
||||
|
||||
**buildermgr pkgwatcher** (`pkgwatcher.go` строка 337):
|
||||
```go
|
||||
if _, exists := pkgw.pkgInformer[ns]; exists {
|
||||
return
|
||||
}
|
||||
```
|
||||
Деdup через `pkgw.pkgInformer[ns]`.
|
||||
|
||||
### 2.3 Анализ стратегии удаления
|
||||
|
||||
`NewDefaultManagedNamespaceWatcherConfig` создаёт конфиг с `RemovalStrategy: NamespaceRemovalStrategyTrackOnly`.
|
||||
При `TrackOnly` — `HandleWatcherNamespaceRemoval` вызывает `DefaultNSResolver().RemoveNamespace()` (наш предыдущий фикс), но **не** вызывает `manager.DispatchRemove()` → `subscriber.OnNamespaceRemove()` → `RemoveFunc` не срабатывает.
|
||||
|
||||
Для вызова `RemoveFunc` нужна стратегия `DispatchRemove`.
|
||||
|
||||
### 2.4 Решение: per-namespace context cancellation
|
||||
|
||||
Informers запускаются через `factory.Start(ctx.Done())`. Стандартный способ остановить отдельный informer — отменить контекст, с которым он запущен.
|
||||
|
||||
**Решение:**
|
||||
```go
|
||||
nsCtx, nsCancel := context.WithCancel(ctx)
|
||||
gpm.nsCancels[ns] = nsCancel
|
||||
finformer.Start(nsCtx.Done()) // вместо ctx.Done()
|
||||
```
|
||||
|
||||
При `RemoveNamespace`:
|
||||
```go
|
||||
if cancel, ok := gpm.nsCancels[ns]; ok {
|
||||
cancel() // останавливает goroutines informer factories
|
||||
delete(gpm.nsCancels, ns)
|
||||
}
|
||||
```
|
||||
|
||||
Это чисто и не требует изменения k8s client-go.
|
||||
|
||||
### 2.5 Mutex и thread-safety
|
||||
|
||||
Существующий код в executor types не защищает lister maps мьютексами. Записи в них происходят только при `AddNamespace` (из subscriber goroutine). Добавление `RemoveNamespace` добавляет ещё одну запись из той же goroutine. Race condition с event handlers (которые читают эти maps) — известное ограничение существующего дизайна, не добавляем мьютексы чтобы не выходить за рамки задачи.
|
||||
|
||||
`HTTPTriggerSet` уже имеет `informerMu sync.RWMutex` — его и используем в `RemoveNamespace` при удалении из `triggerInformer`/`funcInformer`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Реализация — пошаговое описание
|
||||
|
||||
### Шаг 1: `pkg/executor/executortype/executortype.go`
|
||||
|
||||
Добавлен метод в `ExecutorType` interface:
|
||||
|
||||
```go
|
||||
// RemoveNamespace deregisters a namespace from the executor, cancelling its
|
||||
// informer goroutines and clearing dedup state so that a re-add works correctly.
|
||||
// Called when a Namespace with label fission.io/managed=true is removed.
|
||||
RemoveNamespace(ctx context.Context, ns string) error
|
||||
```
|
||||
|
||||
**Почему:** Все три executor type реализуют этот интерфейс. Добавление в интерфейс гарантирует, что новый тип executor не забудет реализовать метод (компилятор поймает).
|
||||
|
||||
---
|
||||
|
||||
### Шаг 2: `pkg/executor/executortype/poolmgr/gpm.go`
|
||||
|
||||
**2a. Добавлено поле в struct:**
|
||||
```go
|
||||
// nsCancels holds per-namespace context cancel functions so informer
|
||||
// factories started in AddNamespace can be stopped on RemoveNamespace.
|
||||
nsCancels map[string]context.CancelFunc
|
||||
```
|
||||
|
||||
**2b. Инициализация в `MakeGenericPoolManager`:**
|
||||
```go
|
||||
nsCancels: make(map[string]context.CancelFunc),
|
||||
```
|
||||
|
||||
**2c. Изменение в `AddNamespace`:** вместо `ctx.Done()` передаём `nsCtx.Done()`:
|
||||
```go
|
||||
nsCtx, nsCancel := context.WithCancel(ctx)
|
||||
gpm.nsCancels[ns] = nsCancel
|
||||
finformer.Start(nsCtx.Done())
|
||||
gpmInformer.Start(nsCtx.Done())
|
||||
```
|
||||
|
||||
**2d. Новый метод `RemoveNamespace`:**
|
||||
```go
|
||||
func (gpm *GenericPoolManager) RemoveNamespace(ctx context.Context, ns string) error {
|
||||
if ns == "" { return nil }
|
||||
gpm.logger.Info("RemoveNamespace: cleaning up namespace (poolmgr)", ...)
|
||||
if cancel, ok := gpm.nsCancels[ns]; ok {
|
||||
cancel()
|
||||
delete(gpm.nsCancels, ns)
|
||||
}
|
||||
delete(gpm.podLister, ns)
|
||||
delete(gpm.podListerSynced, ns)
|
||||
gpm.poolPodC.RemoveNamespace(ns) // очищает envLister/podLister в PoolPodController
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Шаг 3: `pkg/executor/executortype/poolmgr/poolpodcontroller.go`
|
||||
|
||||
Добавлен метод, очищающий lister maps в `PoolPodController`:
|
||||
|
||||
```go
|
||||
func (p *PoolPodController) RemoveNamespace(ns string) {
|
||||
delete(p.envLister, ns)
|
||||
delete(p.envListerSynced, ns)
|
||||
delete(p.podLister, ns)
|
||||
delete(p.podListerSynced, ns)
|
||||
p.logger.Info("PoolPodController.RemoveNamespace: cleared lister state", ...)
|
||||
}
|
||||
```
|
||||
|
||||
**Почему отдельный метод:** `PoolPodController` — отдельная структура внутри poolmgr. Доступ к её полям из `GenericPoolManager.RemoveNamespace` требовал бы либо экспорта полей, либо метода. Метод — чище.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 4: `pkg/executor/executortype/newdeploy/newdeploymgr.go`
|
||||
|
||||
Аналогично gpm:
|
||||
- Добавлен `nsCancels map[string]context.CancelFunc` в struct `NewDeploy`
|
||||
- Инициализирован в `MakeNewDeploy`
|
||||
- `AddNamespace` переключён на `nsCtx.Done()`
|
||||
- Добавлен `RemoveNamespace` очищающий `deplLister`, `deplListerSynced`, `svcLister`, `svcListerSynced`
|
||||
|
||||
---
|
||||
|
||||
### Шаг 5: `pkg/executor/executortype/container/containermgr.go`
|
||||
|
||||
Аналогично. Struct `Container` получил `nsCancels`. `AddNamespace` использует `nsCtx.Done()`. `RemoveNamespace` очищает `deplLister`, `deplListerSynced`, `svcLister`, `svcListerSynced`.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 6: `pkg/executor/multitenant/namespace_subscriber.go`
|
||||
|
||||
Добавлен `RemoveFunc` в `NamespaceSubscriberFuncs`:
|
||||
|
||||
```go
|
||||
RemoveFunc: func(ctx context.Context, record utils.NamespaceRecord) error {
|
||||
return deregisterNamespace(ctx, logger, record.Name, executorTypes)
|
||||
},
|
||||
```
|
||||
|
||||
`deregisterNamespace` итерирует все executor types и вызывает `et.RemoveNamespace(ctx, ns)`.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 7: `pkg/executor/multitenant/ns_watcher.go`
|
||||
|
||||
**7a. Добавлен `deregisterNamespace`:**
|
||||
```go
|
||||
func deregisterNamespace(ctx, logger, ns, executorTypes) error {
|
||||
var joinErr error
|
||||
for _, et := range executorTypes {
|
||||
if err := et.RemoveNamespace(ctx, ns); err != nil {
|
||||
joinErr = errors.Join(joinErr, err)
|
||||
}
|
||||
}
|
||||
logger.Info("multitenant.NSWatcher: deregistered namespace", ...)
|
||||
return joinErr
|
||||
}
|
||||
```
|
||||
|
||||
**7b. Изменён `StartNSWatcher`:** стратегия `TrackOnly` → `DispatchRemove`:
|
||||
```go
|
||||
config := utils.NewDefaultManagedNamespaceWatcherConfig(...)
|
||||
config.RemovalStrategy = utils.NamespaceRemovalStrategyDispatchRemove
|
||||
```
|
||||
|
||||
**Почему:** Без `DispatchRemove` `RemoveFunc` подписчика никогда не вызывается. `TrackOnly` вызывает только `DefaultNSResolver().RemoveNamespace()` (что сделано в `HandleWatcherNamespaceRemoval`), но не диспетчирует событие подписчикам.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 8: `pkg/buildermgr/envwatcher.go`
|
||||
|
||||
- Добавлен `nsCancels map[string]context.CancelFunc` в struct `environmentWatcher`
|
||||
- Инициализирован в `makeEnvironmentWatcher` (там же где `envWatchInformer`)
|
||||
- `AddNamespace` переключён на per-NS context:
|
||||
```go
|
||||
nsCtx, nsCancel := context.WithCancel(ctx)
|
||||
envw.nsCancels[ns] = nsCancel
|
||||
factory.Start(nsCtx.Done())
|
||||
```
|
||||
- Добавлен `RemoveNamespace(ns string)`:
|
||||
```go
|
||||
func (envw *environmentWatcher) RemoveNamespace(ns string) {
|
||||
if cancel, ok := envw.nsCancels[ns]; ok { cancel(); delete(...) }
|
||||
delete(envw.envWatchInformer, ns)
|
||||
}
|
||||
```
|
||||
|
||||
**Ошибка при первой попытке:** replace_string_in_file добавил `nsCancels` с тройным отступом (три таба вместо двух) и без закрывающего `}` struct literal — синтаксическая ошибка компиляции. Исправлено вторым вызовом replace.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 9: `pkg/buildermgr/pkgwatcher.go`
|
||||
|
||||
Аналогично envwatcher:
|
||||
- `nsCancels` в struct `packageWatcher`
|
||||
- Инициализация в `makePackageWatcher`
|
||||
- `AddNamespace` → per-NS ctx для `fissionFactory.Start()` и `podFactory.Start()`
|
||||
- `RemoveNamespace(ns string)` очищает `pkgInformer[ns]`, `podInformer[ns]`
|
||||
|
||||
---
|
||||
|
||||
### Шаг 10: `pkg/buildermgr/namespace_subscriber.go`
|
||||
|
||||
Добавлены два новых интерфейса:
|
||||
```go
|
||||
type builderEnvNamespaceRemover interface {
|
||||
RemoveNamespace(ns string)
|
||||
}
|
||||
type builderPkgNamespaceRemover interface {
|
||||
RemoveNamespace(ns string)
|
||||
}
|
||||
```
|
||||
|
||||
Добавлен `RemoveFunc`:
|
||||
```go
|
||||
RemoveFunc: func(ctx context.Context, record utils.NamespaceRecord) error {
|
||||
deregisterBuilderNamespace(record.Name, envw, pkgw)
|
||||
return nil
|
||||
},
|
||||
```
|
||||
|
||||
`deregisterBuilderNamespace` через type assertion вызывает `RemoveNamespace` если интерфейс реализован:
|
||||
```go
|
||||
func deregisterBuilderNamespace(namespace string, envw, pkgw) {
|
||||
utils.DefaultNSResolver().RemoveNamespace(namespace)
|
||||
if r, ok := envw.(builderEnvNamespaceRemover); ok { r.RemoveNamespace(namespace) }
|
||||
if r, ok := pkgw.(builderPkgNamespaceRemover); ok { r.RemoveNamespace(namespace) }
|
||||
}
|
||||
```
|
||||
|
||||
**Почему type assertion:** `builderEnvNamespaceAdder` — интерфейс-параметр функции `NewNamespaceSubscriber`. Вместо добавления `RemoveNamespace` в существующий интерфейс (что сломало бы тестовые фейки) используем опциональный интерфейс через type assertion.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 11: `pkg/buildermgr/ns_watcher.go`
|
||||
|
||||
Стратегия изменена на `DispatchRemove` аналогично executor.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 12: `pkg/router/httpTriggers.go`
|
||||
|
||||
**12a. Добавлен `nsCancels` в struct:**
|
||||
```go
|
||||
// nsCancels holds per-namespace context cancel functions for informer lifecycle.
|
||||
nsCancels map[string]context.CancelFunc
|
||||
```
|
||||
|
||||
**12b. Инициализация в `makeHTTPTriggerSet`:**
|
||||
```go
|
||||
nsCancels: make(map[string]context.CancelFunc),
|
||||
```
|
||||
|
||||
**12c. Изменён `AddNamespace`:** per-NS ctx:
|
||||
```go
|
||||
nsCtx, nsCancel := context.WithCancel(ctx)
|
||||
ts.nsCancels[ns] = nsCancel
|
||||
factory.Start(nsCtx.Done())
|
||||
k8sCache.WaitForCacheSync(nsCtx.Done(), ...) // тоже nsCtx
|
||||
```
|
||||
|
||||
**12d. Новый метод `RemoveNamespace`:**
|
||||
```go
|
||||
func (ts *HTTPTriggerSet) RemoveNamespace(ns string) {
|
||||
if cancel, ok := ts.nsCancels[ns]; ok { cancel(); delete(...) }
|
||||
ts.informerMu.Lock()
|
||||
delete(ts.triggerInformer, ns)
|
||||
delete(ts.funcInformer, ns)
|
||||
ts.informerMu.Unlock()
|
||||
ts.syncTriggers() // немедленно перестраивает routing table без удалённого NS
|
||||
}
|
||||
```
|
||||
|
||||
**Почему `informerMu.Lock()`:** `HTTPTriggerSet` уже имеет `informerMu sync.RWMutex` для защиты `triggerInformer`/`funcInformer`. Используем его — не добавляем новые мьютексы.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 13: `pkg/router/namespace_subscriber.go`
|
||||
|
||||
Добавлен `routerNamespaceRemover` interface и `RemoveFunc`:
|
||||
|
||||
```go
|
||||
type routerNamespaceRemover interface {
|
||||
RemoveNamespace(ns string)
|
||||
}
|
||||
|
||||
RemoveFunc: func(ctx context.Context, record utils.NamespaceRecord) error {
|
||||
if r, ok := ts.(routerNamespaceRemover); ok {
|
||||
r.RemoveNamespace(record.Name)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Шаг 14: `pkg/router/ns_watcher.go`
|
||||
|
||||
Стратегия → `DispatchRemove`.
|
||||
|
||||
---
|
||||
|
||||
### Шаг 15: Тест-фейк `pkg/executor/multitenant/ns_watcher_test.go`
|
||||
|
||||
`fakeExecutorType` не реализовывал новый метод → ошибка компиляции:
|
||||
```
|
||||
*fakeExecutorType does not implement executortype.ExecutorType (missing method RemoveNamespace)
|
||||
```
|
||||
|
||||
Добавлена заглушка:
|
||||
```go
|
||||
func (f *fakeExecutorType) RemoveNamespace(ctx context.Context, ns string) error { return nil }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Результат компиляции и тестов
|
||||
|
||||
```
|
||||
go build ./pkg/... ./cmd/... → нет вывода (успех)
|
||||
|
||||
go test ./pkg/utils/...
|
||||
./pkg/executor/...
|
||||
./pkg/buildermgr/...
|
||||
./pkg/router/...
|
||||
|
||||
ok github.com/fission/fission/pkg/utils
|
||||
ok github.com/fission/fission/pkg/executor/executortype/newdeploy
|
||||
ok github.com/fission/fission/pkg/executor/executortype/poolmgr
|
||||
ok github.com/fission/fission/pkg/executor/fscache
|
||||
ok github.com/fission/fission/pkg/executor/multitenant
|
||||
ok github.com/fission/fission/pkg/executor/util
|
||||
ok github.com/fission/fission/pkg/buildermgr
|
||||
ok github.com/fission/fission/pkg/router
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Схема потока при удалении NS (после всех правок)
|
||||
|
||||
```
|
||||
k8s: Namespace label fission.io/managed=true удалён/NS удалён
|
||||
│
|
||||
▼
|
||||
ManagedNamespaceWatcher (DispatchRemove стратегия)
|
||||
│
|
||||
├─► HandleWatcherNamespaceRemoval()
|
||||
│ DefaultNSResolver().RemoveNamespace(ns) ← сброс глобального guard
|
||||
│ manager.DispatchRemove(ctx, ns)
|
||||
│
|
||||
▼
|
||||
inMemoryNamespaceManager.DispatchRemove()
|
||||
│
|
||||
├─► goroutine: subscriber[executor].OnNamespaceRemove(record)
|
||||
│ deregisterNamespace(ctx, logger, ns, executorTypes)
|
||||
│ gpm.RemoveNamespace(ctx, ns)
|
||||
│ nsCancel() ← останавливает informer goroutines
|
||||
│ delete(podLister[ns])
|
||||
│ delete(podListerSynced[ns])
|
||||
│ poolPodC.RemoveNamespace(ns)
|
||||
│ delete(envLister[ns])
|
||||
│ delete(envListerSynced[ns])
|
||||
│ delete(podLister[ns])
|
||||
│ delete(podListerSynced[ns])
|
||||
│ deploy.RemoveNamespace(ctx, ns)
|
||||
│ nsCancel()
|
||||
│ delete(deplLister[ns])
|
||||
│ delete(deplListerSynced[ns])
|
||||
│ delete(svcLister[ns])
|
||||
│ delete(svcListerSynced[ns])
|
||||
│ container.RemoveNamespace(ctx, ns)
|
||||
│ nsCancel()
|
||||
│ delete(deplLister[ns])
|
||||
│ delete(svcLister[ns])
|
||||
│
|
||||
├─► goroutine: subscriber[buildermgr].OnNamespaceRemove(record)
|
||||
│ deregisterBuilderNamespace(ns, envw, pkgw)
|
||||
│ DefaultNSResolver().RemoveNamespace(ns) ← повторно (безопасно)
|
||||
│ envw.RemoveNamespace(ns)
|
||||
│ nsCancel()
|
||||
│ delete(envWatchInformer[ns])
|
||||
│ pkgw.RemoveNamespace(ns)
|
||||
│ nsCancel()
|
||||
│ delete(pkgInformer[ns])
|
||||
│ delete(podInformer[ns])
|
||||
│
|
||||
└─► goroutine: subscriber[router].OnNamespaceRemove(record)
|
||||
ts.RemoveNamespace(ns)
|
||||
nsCancel() ← останавливает triggerInf/funcInf goroutines
|
||||
informerMu.Lock()
|
||||
delete(triggerInformer[ns])
|
||||
delete(funcInformer[ns])
|
||||
informerMu.Unlock()
|
||||
syncTriggers() ← немедленно убирает routes для удалённого NS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Что НЕ было реализовано и почему
|
||||
|
||||
**`FunctionServiceCache.DeleteByNamespace(ns string)`** — не реализовано.
|
||||
|
||||
Причина: `idleObjectReaper` периодически вызывает `IsValid()` для всех записей. Для удалённого NS k8s API возвращает 404/403 → `IsValid()` вернёт `false` → запись будет удалена reaperом естественным образом. Это создаёт несколько минут "грязных" записей и 404 ошибки в логах, но не влияет на корректность: для удалённого NS новые запросы не придут (router очистил routes), а reaper уберёт старые записи.
|
||||
|
||||
Реализация `DeleteByNamespace` потребовала бы добавления namespace-индекса в `byFunction`/`byAddress`/`byFunctionUID` кэшах (нетривиально), или дорогого линейного прохода по всем записям. Не было делать без явного запроса.
|
||||
|
||||
---
|
||||
|
||||
## 7. Затронутые файлы (17 изменённых)
|
||||
|
||||
| Файл | Тип изменения |
|
||||
|------|---------------|
|
||||
| `pkg/executor/executortype/executortype.go` | +метод в interface |
|
||||
| `pkg/executor/executortype/poolmgr/gpm.go` | +поле nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/executor/executortype/poolmgr/poolpodcontroller.go` | +RemoveNamespace |
|
||||
| `pkg/executor/executortype/newdeploy/newdeploymgr.go` | +поле nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/executor/executortype/container/containermgr.go` | +поле nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/executor/multitenant/namespace_subscriber.go` | +RemoveFunc |
|
||||
| `pkg/executor/multitenant/ns_watcher.go` | +deregisterNamespace, DispatchRemove |
|
||||
| `pkg/executor/multitenant/ns_watcher_test.go` | +RemoveNamespace в fakeExecutorType |
|
||||
| `pkg/buildermgr/namespace_subscriber.go` | +интерфейсы remover, +RemoveFunc, +deregisterBuilderNamespace |
|
||||
| `pkg/buildermgr/envwatcher.go` | +nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/buildermgr/pkgwatcher.go` | +nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/buildermgr/ns_watcher.go` | DispatchRemove |
|
||||
| `pkg/router/httpTriggers.go` | +nsCancels, modify AddNamespace, +RemoveNamespace |
|
||||
| `pkg/router/namespace_subscriber.go` | +routerNamespaceRemover, +RemoveFunc |
|
||||
| `pkg/router/ns_watcher.go` | DispatchRemove |
|
||||
| `doc/FORENSIC_ARCHITECTURE_AUDIT.md` | перемещён из корня (git rename) |
|
||||
| `doc/console-compat-2026-05-15.md` | создан (отдельная задача) |
|
||||
|
||||
---
|
||||
|
||||
## 8. Ошибки в процессе
|
||||
|
||||
### Ошибка 1: Синтаксическая ошибка в envwatcher.go
|
||||
**Что случилось:** При попытке заменить блок инициализации struct добавился `nsCancels:` с тройным отступом и без закрывающей `}`:
|
||||
```
|
||||
// Стало (неверно):
|
||||
enableOwnerReferences: utils.IsOwnerReferencesEnabled(),
|
||||
nsCancels: make(map[string]context.CancelFunc),
|
||||
err := envWatcher.EnvWatchEventHandlers(ctx)
|
||||
// ← пропущена } закрывающая struct literal
|
||||
```
|
||||
|
||||
**Причина:** replace_string_in_file не нашёл точное совпадение с нужным whitespace и применил замену частично некорректно.
|
||||
|
||||
**Исправление:** второй вызов replace_string_in_file с правильным контекстом (включая соседние строки для однозначного совпадения).
|
||||
|
||||
**Вывод компилятора:**
|
||||
```
|
||||
pkg/buildermgr/envwatcher.go:117:6: syntax error: unexpected := in composite literal; possibly missing comma or }
|
||||
```
|
||||
|
||||
### Ошибка 2: Тест-фейк не реализует интерфейс
|
||||
**Что случилось:** После добавления `RemoveNamespace` в interface `ExecutorType`, тест `ns_watcher_test.go` не компилировался:
|
||||
```
|
||||
*fakeExecutorType does not implement executortype.ExecutorType (missing method RemoveNamespace)
|
||||
```
|
||||
|
||||
**Исправление:** добавлена заглушка в `fakeExecutorType`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Инварианты безопасности
|
||||
|
||||
1. `nsCancel()` идемпотентен: повторный вызов не паникует (context package гарантирует это)
|
||||
2. `RemoveNamespace("")` защищён early return во всех реализациях
|
||||
3. `deregisterBuilderNamespace` через type assertion — безопасно если интерфейс не реализован (просто пропускает)
|
||||
4. `routerNamespaceRemover` через type assertion в router subscriber — аналогично
|
||||
5. Goroutines informer factories останавливаются асинхронно после `cancel()` — это нормально, k8s client-go гарантирует graceful shutdown при отмене контекста
|
||||
6. После `RemoveNamespace` и до следующего `AddNamespace` — любые события от k8s для этого NS будут проигнорированы (informers остановлены, listers удалены)
|
||||
@@ -0,0 +1,279 @@
|
||||
# Forensic Architecture Audit: Fission Fork (multitenant, May 2026)
|
||||
|
||||
> Актуальная редакция. Легаси-версия: `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md`
|
||||
> Обновлено: 2026-05-18 после реализации namespace lifecycle hardening.
|
||||
|
||||
---
|
||||
|
||||
## Статус исправлений
|
||||
|
||||
| Риск | Статус | Коммит |
|
||||
|------|--------|--------|
|
||||
| Informer goroutine/FD leak при TrackOnly removal | ✅ ЗАКРЫТ | `4eedf95f` |
|
||||
| Router stale routes при повторном добавлении NS | ✅ ЗАКРЫТ | `4eedf95f` |
|
||||
| Executor dedup dirty state при re-add NS | ✅ ЗАКРЫТ | `4eedf95f` |
|
||||
| Синхронный subscriber dispatch (onboarding latency) | ✅ ЗАКРЫТ | предыдущая сессия |
|
||||
| `DefaultNSResolver` только append (нет RemoveNamespace) | ✅ ЗАКРЫТ | предыдущая сессия |
|
||||
| Stuck-failed namespace без auto-recovery | ✅ ЗАКРЫТ | `919e8439` |
|
||||
| AdoptExistingResources race при rolling update | ✅ ЗАКРЫТ | `2a7d6101` |
|
||||
| No Explicit State Machine (implicit phase transitions) | ⚠️ СМЯГЧЕНО | `919e8439` |
|
||||
| Sharded mutex (bottleneck при >500 concurrent tenant) | ⏳ BACKLOG | не актуально при текущей нагрузке |
|
||||
|
||||
---
|
||||
|
||||
## Architectural Decisions (реально принятые)
|
||||
|
||||
- **Dynamic Namespace Discovery**: Механизм динамического обнаружения и подключения tenant-namespace через label `fission.io/managed=true` (`pkg/utils/namespace_manager.go`, `pkg/executor/multitenant/ns_watcher.go`).
|
||||
- **Namespace Lifecycle Management**: Жизненный цикл namespace централизован через интерфейс `NamespaceManager` с подписчиками (executor, router, buildermgr).
|
||||
- **Decoupled Registration**: Каждый компонент подписывается как `NamespaceSubscriber` и реализует свою логику инициализации/чистки ресурсов.
|
||||
- **Backward Compatibility**: Поддержка статического списка через env (`FISSION_RESOURCE_NAMESPACES`) с динамическим расширением.
|
||||
- **No-Restart Onboarding**: Добавление tenant не требует рестарта pod-ов.
|
||||
- **RBAC/SA Provisioning**: Автоматическое создание SA и RBAC для новых namespace (`EnsureNamespaceSA`).
|
||||
- **Informer Factories Per Namespace**: Отдельная informer factory для каждого NS, с per-NS context cancellation.
|
||||
- **Explicit Namespace Removal Strategy**: `DispatchRemove` — при удалении NS вызываются `RemoveFunc` у всех подписчиков, останавливаются informer-ы через `context.CancelFunc`.
|
||||
- **Parallel Subscriber Dispatch**: Подписчики вызываются параллельно через `errgroup` — onboarding не блокируется медленным SA provisioning.
|
||||
|
||||
---
|
||||
|
||||
## Core Complexity Centers
|
||||
|
||||
- **NamespaceManager & Watcher**: Центр всей динамики — координация событий, фаз, подписчиков.
|
||||
- **ExecutorType Subsystems**: Poolmgr, NewDeploy, Container — каждый хранит собственный per-NS кэш, lister-ы, логику adoption и reaping.
|
||||
- **Informer Lifecycle**: Динамическое создание/остановка informer-ов через per-NS `context.CancelFunc`. Чистка `envLister[ns]`/`deplLister[ns]`/`triggerInformer[ns]` при `RemoveNamespace`.
|
||||
- **FunctionServiceCache**: Кэширование и lifecycle function pod-ов, синхронизация с событиями из разных источников. **Не очищается при RemoveNamespace** — `idleObjectReaper` убирает устаревшие записи через `IsValid()` check.
|
||||
|
||||
---
|
||||
|
||||
## Hidden Coupling & Accidental Complexity
|
||||
|
||||
- **Implicit Contract**: Все компоненты обязаны реализовывать `NamespaceSubscriber` симметрично (и `AddFunc`, и `RemoveFunc`). Нарушение → silent drift.
|
||||
- **Global vs Local State**: Глобальный `DefaultNSResolver` + локальные lister-ы в каждом executor type. `RemoveNamespace` в NSResolver и в каждом executor type должны быть вызваны согласованно.
|
||||
- **Deduplication Responsibility**: `AddNamespace` дедупликация — через `DefaultNSResolver().AddNamespace()` возвращающий `bool`, и через проверку локального lister-а (`envLister[ns] != nil`). После `RemoveNamespace` оба guard сбрасываются → re-add корректно создаёт новые informer-ы.
|
||||
- **Event Handler Ordering**: Порядок подписчиков в `Subscribe` влияет на side-effects, но `errgroup` делает их параллельными — ordering больше не определяет latency, но всё ещё влияет на приоритет ошибок.
|
||||
- **RBAC Drift**: Provisioning SA/RBAC в `registerNamespace`, cleanup — в `deregisterNamespace`. При сбое cleanup — dangling SA/ClusterRoleBinding.
|
||||
|
||||
---
|
||||
|
||||
## Workaround-Driven Decisions
|
||||
|
||||
- ~~**Track-Only Removal**~~ → **ЗАМЕНЕНО** на `DispatchRemove` — cleanup вызывается всегда.
|
||||
- **Manual Adoption**: При старте executor-ы делают adopt orphaned ресурсов. Закрыто: `PreRegisterManagedNamespaces` обеспечивает полный NS snapshot до adopt/cleanup (§1.3).
|
||||
- **Explicit Reaper Loops**: `idleObjectReaper` чистит `FunctionServiceCache` вместо event-driven подхода. Приемлемо: `IsValid()` check достаточен при корректной работе per-NS informer-ов.
|
||||
|
||||
---
|
||||
|
||||
## Fragile Operational Components
|
||||
|
||||
- **RBAC/SA Drift**: Неконсистентность между созданием и удалением SA/ролей при сбое в `deregisterNamespace`.
|
||||
- **Cache Invalidation**: `FunctionServiceCache` не очищается при `RemoveNamespace` — расчёт на `idleObjectReaper`. При высоком churn rate может накапливать stale записи быстрее, чем reaper убирает.
|
||||
- **Adoption Race**: ~~`AdoptExistingResources` vs `namespace_subscriber` — активная проблема~~ — закрыто: `PreRegisterManagedNamespaces` перед adopt/cleanup (`2a7d6101`).
|
||||
- **Stuck Failed Phase**: ~~Namespace в `failed` не восстанавливается без рестарта~~ — закрыто: `RunReconciler` + полная цепочка error propagation (§1.4).
|
||||
|
||||
---
|
||||
|
||||
## Poor Scalability Risks
|
||||
|
||||
- **Informer Explosion**: ~1500–2000 goroutine при 100 tenant (см. §2). **Частично смягчено**: goroutine-ы корректно останавливаются при `RemoveNamespace` — нет накопления при churn. Но в steady-state 100 NS — линейный рост горутин остаётся.
|
||||
- ~~**Synchronous Dispatch**~~ → **ИСПРАВЛЕНО**: параллельный dispatch через `errgroup`.
|
||||
- **Centralized Locking**: Глобальный mutex на NamespaceManager. При текущей нагрузке (<50 ns) — не узкое место. При >500 concurrent tenant — backlog (sharded mutex, §5).
|
||||
- **Thundering Herd на resync**: 100 NS × 5 informer-типов × LIST каждые 30 мин — 500 concurrent LIST к API.
|
||||
|
||||
---
|
||||
|
||||
## Future Maintenance Problems
|
||||
|
||||
- **Hidden State Machines**: Фазы namespace реализованы неявно — сложно дебажить stuck state. Нет формализованной машины состояний с explicit transitions.
|
||||
- **Implicit Error Handling**: Ошибки в `deregisterNamespace` логируются, но NS может остаться в некорректном состоянии. Нет `NamespaceCondition` на k8s-объекте.
|
||||
- **Contract Drift**: Изменение интерфейса `NamespaceSubscriber` (например, добавление `ResyncFunc`) требует синхронного обновления всех компонентов.
|
||||
- **FunctionServiceCache без per-NS cleanup**: если `idleObjectReaper` будет отключён/изменён — stale cache может накапливаться.
|
||||
|
||||
---
|
||||
|
||||
## Risky / Hard-to-Maintain Decisions
|
||||
|
||||
| Решение | Статус | Примечание |
|
||||
|---------|--------|------------|
|
||||
| Informer Lifecycle Management | ✅ Hardened | per-NS context cancel + RemoveNamespace во всех компонентах |
|
||||
| Centralized Mutex | ⚠️ Приемлемо | sharding в backlog, не актуально до >500 NS |
|
||||
| Manual Adoption | ✅ Закрыто | race при rolling update (`2a7d6101`) |
|
||||
| No Explicit State Machine | ✅ Частично закрыто | stuck-failed закрыт (`919e8439`); явная state machine в backlog |
|
||||
| Eventual Consistency | ⚠️ Смягчено | параллельный dispatch уменьшает окно, но не устраняет |
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenancy, Isolation, Orchestration, Lifecycle, State, Reconciliation
|
||||
|
||||
- **Multi-Tenancy**: Label-based discovery, каждый tenant — отдельный namespace, изоляция на уровне k8s.
|
||||
- **Isolation Model**: Namespace-level isolation, per-NS SA/RBAC, per-NS informer factory.
|
||||
- **Lifecycle Management**: Фазы (discovered → registering → active → deregistering → removed / failed) реализованы. Auto-recovery из failed работает через `RunReconciler`. Явная state machine в backlog.
|
||||
- **State Handling**: Глобальный `DefaultNSResolver` + локальные lister-ы. После `RemoveNamespace` — оба синхронизованы. После re-add — оба корректно инициализируются заново.
|
||||
- **Reconciliation Logic**: Каждый компонент через subscribe. Отсутствует reconcile-очередь для failed state.
|
||||
- **Operational Burden**: Средний — goroutine leak устранён, stale informer устранён, stuck-failed закрыт. Требуется мониторинг: orphaned SA/RBAC при неудачном deregister.
|
||||
|
||||
---
|
||||
|
||||
## Engineering Maturity
|
||||
|
||||
- **Maturity**: Архитектурно зрелый, хорошо документированный, с явным reasoning и поэтапным внедрением.
|
||||
- **Complexity**: Высокая в синхронизации и lifecycle. Снижена за счёт формализации `RemoveNamespace` контракта.
|
||||
- **Maintainability**: Среднесрочная — без явной state machine сложность будет расти. Auto-recovery из failed работает.
|
||||
- **Production-Grade**: Близко — informer lifecycle корректен, dispatch параллелен, cleanup симметричен, stuck-failed закрыт, AdoptExistingResources race закрыт.
|
||||
|
||||
---
|
||||
|
||||
# Deep Risk Analysis (актуальная, May 2026)
|
||||
|
||||
---
|
||||
|
||||
## 1. Сценарии отказа
|
||||
|
||||
### 1.1 Informer Lifecycle Management — ✅ ЗАКРЫТ
|
||||
|
||||
**Что было:** relabel-цикл NS создавал phantom-состояние: informer-ы не останавливались при track-only removal, `DefaultNSResolver` не очищал запись → re-add возвращал `false` → новые informer-ы не создавались.
|
||||
|
||||
**Что сделано (коммит `4eedf95f`):**
|
||||
- `RemoveNamespace(ns)` добавлен в интерфейс `ExecutorType` и реализован в poolmgr, newdeploy, container.
|
||||
- В каждом executor type: per-NS context cancel (`nsCancels map[string]context.CancelFunc`). `AddNamespace` создаёт `nsCtx, nsCancel := context.WithCancel(ctx)`, передаёт `nsCtx` в `factory.Start()`. `RemoveNamespace` вызывает `nsCancel()` и удаляет lister-ы из карт.
|
||||
- Router: `HTTPTriggerSet.RemoveNamespace()` отменяет per-NS ctx, удаляет `triggerInformer[ns]`/`funcInformer[ns]` под `informerMu.Lock()`, вызывает `syncTriggers()`.
|
||||
- Buildermgr: `envWatcher.RemoveNamespace()` и `pkgWatcher.RemoveNamespace()` — аналогично.
|
||||
- `DefaultNSResolver.RemoveNamespace(ns)` удаляет NS из глобального map → re-add корректно проходит guard.
|
||||
- Стратегия `DispatchRemove` во всех 3 компонентах → `RemoveFunc` вызывается при удалении NS.
|
||||
|
||||
**Текущий статус:** informer goroutine/FD корректно останавливаются; re-add NS создаёт чистые informer-ы; router не видит stale routes.
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Centralized Mutex — ⚠️ ПРИЕМЛЕМО
|
||||
|
||||
**Сценарий:** высокая churn + concurrent Snapshot.
|
||||
|
||||
`dispatch()` отпускает mutex перед вызовом каждого subscriber, берёт снова для следующего. При батч-онбординге 10+ NS параллельно: конкуренция за mutex, latency spike на `Snapshot()` в `idleObjectReaper`.
|
||||
|
||||
**Смягчено:** `dispatch()` теперь параллельный (errgroup) — подписчики не вызываются последовательно, время блокировки mutex между подписчиками устранено. `Snapshot()` конкурирует только с `Upsert` — при текущей нагрузке (<50 NS) практически нет.
|
||||
|
||||
**Остаётся:** при >500 concurrent tenant с >1 onboarding/sec — sharded mutex даст выигрыш. В backlog.
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Manual Adoption (AdoptExistingResources) — ✅ ЗАКРЫТ (коммит `2a7d6101`)
|
||||
|
||||
**Сценарий: гонка adoption vs watcher при старте**
|
||||
|
||||
**Что было:** `AdoptExistingResources` и `CleanupOldExecutorObjects` запускались до `StartNSWatcher`. `DefaultNSResolver().Snapshot()` возвращал только статические NS из `FISSION_RESOURCE_NAMESPACES` → managed NS не покрывались:
|
||||
- Pods от предыдущего executor в managed NS не adoptировались (сохраняли старый `instanceID`) → poolmgr создавал новые pool pods → cold start.
|
||||
- Старые RS/deployments в managed NS не чистились → накапливались.
|
||||
|
||||
**Что сделано:** `multitenant.PreRegisterManagedNamespaces(ctx, logger, kubernetesClient)` — синхронный `Namespaces.List` с label `fission.io/managed=true` вызывается в `executor.go` **до** goroutines adopt+cleanup. Добавляет все managed NS в `DefaultNSResolver`. Идемпотентен с последующим `AddFunc` из watcher. Не ломает при ошибке API (warn + proceed).
|
||||
|
||||
**End-to-end после фикса:**
|
||||
1. `PreRegisterManagedNamespaces` → `DefaultNSResolver` содержит static + managed NS
|
||||
2. `AdoptExistingResources` → патчит pods в managed NS с новым `instanceID`
|
||||
3. `CleanupOldExecutorObjects` / `GetReaperNamespace()` → видит managed NS → чистит стale объекты
|
||||
4. `StartNSWatcher` → `AddFunc` срабатывает для тех же NS — `DefaultNSResolver().AddNamespace()` idempotent, `AddNamespace` executor types dedup-protected
|
||||
|
||||
---
|
||||
|
||||
### 1.4 No Explicit State Machine — ✅ ЗАКРЫТ (коммит `919e8439`)
|
||||
|
||||
**Сценарий: stuck в `failed` без auto-recovery**
|
||||
|
||||
**Что было:** `EnsureNamespaceSA` и `registerNamespace` были void-функциями — ошибки только логировались, до `MarkPartFailed` не доходили. Executor subscriber всегда возвращал nil → namespace никогда не попадал в `NamespacePhaseFailed` → `RunReconciler` для executor был мёртвым кодом.
|
||||
|
||||
**Что сделано:**
|
||||
- `setupSAAndRoleBindings` → возвращает `error`
|
||||
- `EnsureNamespaceSA` → возвращает `error`, пробрасывает
|
||||
- `registerNamespace` → возвращает `error` (SA + executorTypes) с `fmt.Errorf` wrapping
|
||||
- Executor `AddFunc`/`ResyncFunc` → пробрасывают ошибку вместо `return nil`
|
||||
- `RunReconciler` → принимает `*zap.Logger`, логирует каждый retry и исход
|
||||
|
||||
**End-to-end flow:**
|
||||
1. `EnsureNamespaceSA` fails (k8s 503) → `registerNamespace` returns error
|
||||
2. Executor AddFunc returns error → `dispatch()` → `MarkPartFailed("executor")`
|
||||
3. `deriveNamespacePhase` → `NamespacePhaseFailed`
|
||||
4. `RunReconciler` tick (30s) находит namespace → `DispatchResync` → retry
|
||||
5. Если API восстановился: `MarkPartActive` → `NamespacePhaseActive` → лог `resync succeeded`
|
||||
|
||||
**Накопление при churn:** ликвидировано — failed NS автоматически выходят из этой фазы при восстановлении API.
|
||||
|
||||
**Ограничение:** нет max-retries. Namespace, у которого SA создать принципиально невозможно (например, удалённый k8s namespace), будет ретраиться вечно. Приемлемо на текущем масштабе.
|
||||
|
||||
---
|
||||
|
||||
### 1.5 Eventual Consistency — ⚠️ СМЯГЧЕНО
|
||||
|
||||
**Сценарий:** HTTPTrigger создан в окне до готовности informer.
|
||||
|
||||
**Было:** последовательный dispatch → если executor делал SA provisioning 10–30 сек, router не начинал `WaitForCacheSync`. Trigger, созданный в этом окне, пропускался до следующего resync (30 мин).
|
||||
|
||||
**Смягчено:** параллельный dispatch через errgroup → router и executor стартуют `AddNamespace` одновременно. Окно уязвимости = время `WaitForCacheSync` в router (~2–5 сек), а не время SA provisioning (~30 сек).
|
||||
|
||||
**Остаётся:** trigger, созданный за 2–5 сек до `WaitForCacheSync` в router → нормально обрабатывается через `AddFunc` после sync. Фактически проблема устранена для практических сценариев.
|
||||
|
||||
---
|
||||
|
||||
## 2. Анализ при 50–100 tenant с churn 10 ns/час
|
||||
|
||||
### Informer Count (steady-state)
|
||||
|
||||
При 100 активных tenant:
|
||||
- **Poolmgr**: 2 factory × 100 NS × ~3–5 goroutine = **600–1000 goroutine**
|
||||
- **NewDeploy**: аналогично ~600–1000 goroutine
|
||||
- **Router**: 1 factory × 100 NS × ~2 goroutine = **200 goroutine**
|
||||
- **Buildermgr**: ~200 goroutine
|
||||
|
||||
Итого: **~1600–2400 goroutine** от informer-ов. **Линейный рост с числом NS — неизбежен при текущей архитектуре.**
|
||||
|
||||
**Что изменилось после hardening:** при churn goroutine-ы корректно останавливаются при `RemoveNamespace` — нет накопления мёртвых goroutine. Steady-state = ~O(active_NS), а не O(total_NS_ever_seen).
|
||||
|
||||
### Thundering Herd на resync
|
||||
|
||||
100 NS × 5 informer-типов × LIST каждые 30 мин = **500 concurrent LIST** к Kubernetes API. Не изменилось, не исправлено.
|
||||
|
||||
### Stuck Failed Accumulation — ✅ ЗАКРЫТ
|
||||
|
||||
Failed NS автоматически ретраятся `RunReconciler` каждые 30с и выходят из `failed` при восстановлении API. Накопления больше не происходит.
|
||||
|
||||
### AdoptExistingResources Race — ✅ ЗАКРЫТ
|
||||
|
||||
`PreRegisterManagedNamespaces` синхронно добавляет managed NS в `DefaultNSResolver` до adopt/cleanup. Старые pods adoptируются, stale объекты чистятся. Подробно — §1.3.
|
||||
|
||||
---
|
||||
|
||||
## 3. Рекомендации (приоритизированные)
|
||||
|
||||
### P1 — Reconcile-очередь для failed NS — ✅ ЗАКРЫТ (`919e8439`)
|
||||
|
||||
Error propagation исправлена во всей цепочке: `setupSAAndRoleBindings` → `EnsureNamespaceSA` → `registerNamespace` → executor subscriber. `RunReconciler` логирует retry и исход.
|
||||
|
||||
### P2 — AdoptExistingResources после BootstrapAndDispatch — ✅ ЗАКРЫТ (`2a7d6101`)
|
||||
|
||||
`PreRegisterManagedNamespaces` вызывается синхронно до adopt/cleanup. Делает один `Namespaces.List(label=fission.io/managed=true)` → добавляет все managed NS в `DefaultNSResolver`. После этого adopt и cleanup покрывают полный tenant NS set.
|
||||
|
||||
**Влияние:** устранены orphaned pods при холодном старте и resource leak (stale RS/deployments).
|
||||
|
||||
### P3 — NamespaceCondition на k8s Namespace объекте
|
||||
|
||||
Пометить Namespace через `kubectl annotate` или через status subresource при failed phase → оператор видит причину без чтения логов.
|
||||
|
||||
### Backlog — Sharded mutex
|
||||
|
||||
Актуально при >500 concurrent tenant с >1 onboarding/sec. Технически feasible без breaking interface change (см. `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §5`).
|
||||
|
||||
---
|
||||
|
||||
## 4. FunctionServiceCache — текущий инвариант
|
||||
|
||||
`FunctionServiceCache` (`fsCache` в gpm и newdeploy) **не очищается** при `RemoveNamespace`. Это осознанное решение:
|
||||
|
||||
- `idleObjectReaper` периодически вызывает `fsCache.ListOldForPool()` → для каждой записи проверяет `podLister[ns]` → если NS удалён, `podLister[ns]` == nil → pod не найден → запись считается expired → `fsCache.DeleteEntry()`.
|
||||
- Временной лаг = интервал reaper-а (по умолчанию ~1 мин). При высоком churn возможно накопление stale записей, но они не вызывают функциональных ошибок (только небольшой overhead на reaper iteration).
|
||||
|
||||
**Когда станет проблемой:** при отключении/изменении reaper-а или при >10 000 stale записей (O(n) iteration).
|
||||
|
||||
---
|
||||
|
||||
## 5. Sharded Mutex — вердикт
|
||||
|
||||
**Технически реализуемо** без breaking interface change. Полный код — в `FORENSIC_ARCHITECTURE_AUDIT_LEGACY_2026-05.md §5`.
|
||||
|
||||
**Вердикт:** не оправдано при текущей нагрузке. Реальный bottleneck — AdoptExistingResources race — закрыт (`2a7d6101`). Sharded mutex — в backlog, актуально при >500 concurrent tenant с >1 onboarding/sec.
|
||||
@@ -0,0 +1,385 @@
|
||||
# Forensic Architecture Audit: Fission Fork (feature/multitenant, May 2026)
|
||||
|
||||
---
|
||||
|
||||
## Architectural Decisions (реально принятые)
|
||||
- **Dynamic Namespace Discovery**: Введён механизм динамического обнаружения и подключения tenant-namespace через label `fission.io/managed=true` (см. `pkg/utils/namespace_manager.go`, `pkg/executor/multitenant/ns_watcher.go`).
|
||||
- **Namespace Lifecycle Management**: Весь жизненный цикл namespace теперь централизован через интерфейс `NamespaceManager` с подписчиками (executor, router, buildermgr).
|
||||
- **Decoupled Registration**: Каждый компонент (executor, router, buildermgr) подписывается как subscriber и реализует свою логику инициализации/чистки ресурсов при появлении/удалении namespace.
|
||||
- **Backward Compatibility**: Сохраняется поддержка статического списка через env (`FISSION_RESOURCE_NAMESPACES`), но теперь он расширяется динамически.
|
||||
- **No-Restart Onboarding**: Добавление нового tenant не требует рестарта pod-ов — watcher реагирует на label, триггерит регистрацию во всех подсистемах.
|
||||
- **RBAC/SA Provisioning**: Автоматическое создание service account и RBAC для новых namespace (см. `EnsureNamespaceSA`).
|
||||
- **Informer Factories Per Namespace**: Для каждого нового namespace создаются отдельные informer factory для CRD и core-ресурсов.
|
||||
- **Explicit Namespace Removal Strategy**: Поддержка двух стратегий удаления: track-only (по умолчанию) и dispatch-remove (с вызовом OnNamespaceRemove у подписчиков).
|
||||
|
||||
## Core Complexity Centers
|
||||
- **NamespaceManager & Watcher**: Центр всей динамики — сложная координация событий, фаз, подписчиков, race-conditions.
|
||||
- **ExecutorType Subsystems**: Poolmgr, NewDeploy, Container — каждый хранит собственное состояние, кэш, логику adoption и reaping.
|
||||
- **Informer Lifecycle**: Динамическое создание/удаление informer-ов на лету для каждого namespace.
|
||||
- **FunctionServiceCache**: Кэширование и lifecycle function pod-ов, синхронизация с событиями из разных источников.
|
||||
|
||||
## Hidden Coupling & Accidental Complexity
|
||||
- **Implicit Contract**: Все компоненты обязаны корректно реализовать NamespaceSubscriber — нарушение приводит к silent drift.
|
||||
- **Global vs Local State**: Есть глобальный NamespaceResolver и локальные состояния в каждом executor type — возможны рассинхронизации.
|
||||
- **Deduplication Responsibility**: Deduplication namespace размазан между глобальным резолвером и локальными структурами.
|
||||
- **Event Handler Ordering**: Порядок подписчиков влияет на фазу и side-effects, но не гарантируется явно.
|
||||
- **RBAC Drift**: Provisioning SA/RBAC делается в одном месте, но cleanup — в другом, возможны dangling ресурсы.
|
||||
|
||||
## Iterative Growth
|
||||
- **Layered Refactor**: Ветка развивается через серию малых шагов (см. doc/thinking/2026-04-26-namespace-manager-step*.md), каждый шаг — отдельный инвариант.
|
||||
- **Hybrid Model**: Некоторое время coexist старый статический и новый динамический pipeline, с явным разделением путей.
|
||||
- **Feature Flags via Env**: Многое управляется через env-переменные, что позволяет поэтапно включать/выключать новые механики.
|
||||
|
||||
## Workaround-Driven Decisions
|
||||
- **Track-Only Removal**: По умолчанию удаление namespace не вызывает cleanup в подписчиках — workaround против race-condition при массовых удалениях.
|
||||
- **Manual Adoption**: При старте executor-ы делают adopt orphaned ресурсов (pods, deployments) — workaround для несовершенного lifecycle.
|
||||
- **Explicit Reaper Loops**: Для чистки orphaned объектов используются отдельные циклы (object reaper), а не event-driven подход.
|
||||
|
||||
## Fragile Operational Components
|
||||
- **Informer Factory Lifecycle**: Ошибки в динамическом создании/удалении informer-ов приводят к memory leak или stale watchers.
|
||||
- **RBAC/SA Drift**: Неконсистентность между созданием и удалением сервисных аккаунтов и ролей.
|
||||
- **Cache Invalidation**: FunctionServiceCache может рассинхронизироваться при сбоях в event flow.
|
||||
- **Adoption Loops**: AdoptExistingResources может не покрыть все edge-case, особенно при race между startup и watcher.
|
||||
|
||||
## Poor Scalability Risks
|
||||
- **Informer Explosion**: На сотнях/тысячах namespace число informer-ов и goroutine растёт линейно, возможен memory/FD exhaustion.
|
||||
- **Synchronous Dispatch**: Все подписчики вызываются синхронно, при долгой инициализации одного — блокируются остальные.
|
||||
- **Centralized Locking**: NamespaceManager держит глобальный mutex на все операции — bottleneck при высокой churn rate.
|
||||
- **No Sharding**: Нет горизонтального масштабирования NamespaceManager — всё в одном процессе.
|
||||
|
||||
## Future Maintenance Problems
|
||||
- **Hidden State Machines**: Фазы namespace и частей (part state) реализованы неявно, без явной state machine — сложно дебажить stuck state.
|
||||
- **Implicit Error Handling**: Ошибки в подписчиках часто логируются, но не эскалируются — возможна silent failure.
|
||||
- **Contract Drift**: Любое изменение интерфейса NamespaceSubscriber требует синхронного обновления всех компонентов.
|
||||
- **Complex Test Surface**: Много интеграционных точек, сложно покрыть тестами все сценарии гонок и отказов.
|
||||
|
||||
## Deepest Upstream Divergence
|
||||
- **Полная замена статической модели discovery на динамическую через watcher и NamespaceManager.**
|
||||
- **Весь lifecycle tenant-namespace теперь event-driven, а не env-driven.**
|
||||
- **Введён централизованный интерфейс подписки на события namespace для всех core-компонентов.**
|
||||
- **Механика adopt orphaned ресурсов и явная поддержка rollback/cleanup.**
|
||||
|
||||
## Surprisingly Mature Parts
|
||||
- **Интерфейс NamespaceManager**: Чётко выделен, покрыт тестами, поддерживает snapshot, summary, phase tracking.
|
||||
- **Event Handler Abstraction**: Все watcher-ы используют единый event handler contract, легко расширять.
|
||||
- **Backward Compatibility Layer**: Старый pipeline не сломан, coexist с новым.
|
||||
- **Документация и коммиты**: Подробные шаги, объяснения, reasoning — видно зрелый инженерный подход.
|
||||
|
||||
## Risky / Hard-to-Maintain Decisions
|
||||
- **Informer Lifecycle Management**: Очень сложно гарантировать отсутствие leak/stale при динамике.
|
||||
- **Centralized Mutex**: Один mutex на NamespaceManager — риск блокировок.
|
||||
- **Manual Adoption**: AdoptExistingResources — временное решение, не покрывает все сценарии.
|
||||
- **No Explicit State Machine**: Фазы и переходы не формализованы, возможны stuck state.
|
||||
- **Eventual Consistency**: Нет гарантии моментальной консистентности между компонентами.
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tenancy, Isolation, Orchestration, Lifecycle, State, Reconciliation
|
||||
- **Multi-Tenancy**: Реализовано через label-based discovery, каждый tenant — отдельный namespace, все ресурсы изолированы на уровне k8s.
|
||||
- **Isolation Model**: Namespace-level isolation, автоматическое создание SA/RBAC, informer-ы и кэш на каждый tenant.
|
||||
- **Orchestration**: NamespaceManager + подписчики — централизованный event bus для всех core-компонентов.
|
||||
- **Lifecycle Management**: Поддержка всех фаз (discovered, registering, active, deregistering, removed, failed), но state machine неявная.
|
||||
- **State Handling**: Гибрид глобального и локального состояния, возможны рассинхронизации.
|
||||
- **Reconciliation Logic**: Каждый компонент реализует свою reconcile-логику через подписку на события.
|
||||
- **Controller Complexity**: Высокая, много слоёв абстракции, много точек гонок.
|
||||
- **Deployment Reproducibility**: Helm-чарты поддерживают все новые env, backward compatibility сохранён.
|
||||
- **Operational Burden**: Высокий — требуется мониторинг leak, race, orphaned ресурсов, ручной контроль за adoption.
|
||||
|
||||
---
|
||||
|
||||
## Engineering Maturity, Complexity, Maintainability Horizon
|
||||
- **Maturity**: Архитектурно зрелый, хорошо документированный, с явным reasoning и поэтапным внедрением.
|
||||
- **Complexity**: Высокая, особенно в динамике и синхронизации между компонентами.
|
||||
- **Maintainability**: Среднесрочная — без явной state machine и горизонтального масштабирования возможны проблемы при росте нагрузки.
|
||||
- **Production-Grade**: Ближе к production-grade platform engineering, чем к эксперименту, но требует доработки по масштабированию и явной формализации state transitions.
|
||||
|
||||
---
|
||||
|
||||
## Architectural Drift / Entropy / Hazards
|
||||
- **Drift**: Возможен drift между глобальным и локальным состоянием, если подписчики реализованы несимметрично.
|
||||
- **Entropy**: Много точек входа, implicit contract, нет явной state machine — сложность будет расти.
|
||||
- **Hazards**: Memory leak, race-condition, orphaned ресурсы, silent failure при ошибках в подписчиках.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
Этот форк — зрелая попытка перевести Fission на event-driven multi-tenant архитектуру с динамическим discovery и централизованным lifecycle management. Основные сложности и риски — в управлении состоянием, синхронизации и масштабируемости. Требует дальнейшей формализации state machine, горизонтального масштабирования и усиления тестового покрытия для production-grade эксплуатации.
|
||||
|
||||
---
|
||||
|
||||
# Deep Risk Analysis (May 2026)
|
||||
|
||||
> Конкретные сценарии отказа, оценка при 50–100 tenant, предложения по исправлению.
|
||||
|
||||
---
|
||||
|
||||
## 1. Сценарии отказа для каждого "Risky Decision"
|
||||
|
||||
### 1.1 Informer Lifecycle Management
|
||||
|
||||
**Сценарий: повторная регистрация namespace через relabel**
|
||||
|
||||
1. Оператор снимает label `fission.io/managed=true` с namespace `tenant-42`.
|
||||
2. Namespace-watcher вызывает `HandleWatcherNamespaceRemoval()`. Стратегия `TrackOnly`: NamespaceManager помечает запись как `removed` и **не вызывает** `OnNamespaceRemove` у подписчиков.
|
||||
3. Informer-ы executor (gpm, newdeploy) и router продолжают работать — pool для tenant-42 жив, функции маршрутизируются.
|
||||
4. Оператор возвращает label — kubernetes генерирует `MODIFIED`-событие.
|
||||
5. `RunManagedNamespaceWatcher` (resync 30 мин) может не вызвать Add снова для уже известного NS.
|
||||
6. **Router**: `AddNamespace` вызывает `DefaultNSResolver().AddNamespace(ns)`. Глобальный resolver уже содержит tenant-42 (его никто не удалял из-за track-only) → возвращает `false` → router делает **early return без создания новых informer-ов** (строка 460 `httpTriggers.go`). Router считает namespace активным (старые informer-ы ещё работают) — но если они были остановлены контекстом — тихое 404.
|
||||
7. **Executor**: `gpm.AddNamespace` проверяет `poolPodC.envLister[ns]` — если старый lister жив, возвращает nil сразу (дедупликация). Всё выглядит нормально, но фактически используются **устаревшие informer-ы** с застрявшим кэшем.
|
||||
|
||||
**Итог**: relabel-цикл создаёт phantom-состояние: компоненты думают что NS активен, но его lifecycle разорван.
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Centralized Mutex
|
||||
|
||||
**Сценарий: высокая churn + concurrent Snapshot**
|
||||
|
||||
`dispatch()` снимает write-lock перед вызовом каждого subscriber-а, затем берёт его снова для следующего. Структура:
|
||||
|
||||
```
|
||||
mu.Lock() → читаем список subs →
|
||||
mu.Unlock() → вызываем handler(sub1) [k8s API call, может занять сотни мс]
|
||||
mu.Lock() → читаем следующий sub →
|
||||
mu.Unlock() → вызываем handler(sub2)
|
||||
```
|
||||
|
||||
Параллельно: router каждые 20 мс делает `syncTriggers()` → `updateRouter()` → итерирует `snapshotFuncInformers()` → берёт `informerMu.RLock`. Это другой mutex, но `DefaultNSResolver().Snapshot()` вызывается из `idleObjectReaper` каждые 5 сек под глобальным `RWMutex` NamespaceManager.
|
||||
|
||||
При 100 tenant с churn 10 ns/час: в среднем каждые 6 мин добавляется namespace. Само по себе безвредно. Но при пике (батч-онбординг 10 tenant за 1 минуту): `dispatch()` держит write-lock с паузами на unlock/relock для каждого subscriber × 10 параллельных dispatch → конкуренция за mutex возрастает. `Snapshot()` в `idleObjectReaper` (каждые 5 сек) и в `AdoptExistingResources` (каждый рестарт) будут ждать.
|
||||
|
||||
**Итог**: не deadlock, но latency spike на Snapshot на старте и при батч-онбординге — 200–500 мс при 10+ concurrent dispatch.
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Manual Adoption (AdoptExistingResources)
|
||||
|
||||
**Сценарий: гонка adoption vs watcher**
|
||||
|
||||
1. Executor стартует. `AdoptExistingResources` запускается, берёт `DefaultNSResolver().Snapshot()` — snapshot содержит только статические NS из `FISSION_RESOURCE_NAMESPACES`.
|
||||
2. Параллельно запускается `RunManagedNamespaceWatcher`. Watcher вызывает `BootstrapAndDispatch()`, который регистрирует managed NS и вызывает `registerNamespace()` у executor-подписчика.
|
||||
3. `registerNamespace()` вызывает `DefaultNSResolver().AddNamespace(ns)` (глобальный guard), затем `gpm.AddNamespace()`.
|
||||
4. **Но `AdoptExistingResources` уже завершила свой loop** — managed NS не попал в snapshot. Orphaned pods в tenant NS не приняты.
|
||||
5. Функции в этих pod-ах будут вызываться ещё раз через cold start — лишний latency spike и потеря статуса `instanceID` у подов (старый instanceID в annotation не перезаписан → `CleanupOldExecutorObjects` сочтёт их orphaned → удалит).
|
||||
|
||||
**Hardcoded 30s timeout**: `AdoptExistingResources` в poolmgr не имеет явного timeout, но `k8sCache.WaitForCacheSync` в `Run()` блокирует до готовности — только после этого запускается `service()`. Если namespace watcher опередил, poolmgr получит env-события до того как `AdoptExistingResources` завершится → гонка на `gpm.pools` map (не защищена mutex вне `service()` goroutine).
|
||||
|
||||
---
|
||||
|
||||
### 1.4 No Explicit State Machine
|
||||
|
||||
**Сценарий: stuck в `failed` без auto-recovery**
|
||||
|
||||
1. Namespace `tenant-99` помечен `fission.io/managed=true`.
|
||||
2. `registerNamespace()` вызывает `EnsureNamespaceSA()` — Kubernetes API momentarily unavailable (503).
|
||||
3. `EnsureNamespaceSA()` возвращает ошибку → вызывающий код (предположительно) пишет в лог и помечает часть как `NamespacePartStateFailed`.
|
||||
4. `deriveNamespacePhase()` выставляет namespace в `NamespacePhaseFailed`.
|
||||
5. **Нет reconcile-цикла**: нет горутины, которая периодически проверяет failed namespace и пытается повторить. Phase останется `failed` до рестарта процесса.
|
||||
6. Router был вызван следующим в цепочке dispatch. Т.к. dispatch вызывается подписчики последовательно без barrier, router **уже создал свои informer-ы** до того как executor завершился с ошибкой.
|
||||
7. **Dirty state**: router видит `tenant-99` как активный (informer-ы есть), executor — нет (SA/RBAC не создан). Любой вызов функции из tenant-99 → executor не может специализировать pod (нет fetcher SA) → 503.
|
||||
|
||||
Лог покажет ошибку, но namespace останется в `failed` навсегда (до рестарта). Оператор не получит никакого k8s-статуса — ни condition на Namespace объекте, ни event.
|
||||
|
||||
---
|
||||
|
||||
### 1.5 Eventual Consistency
|
||||
|
||||
**Сценарий: HTTPTrigger создан в окне до ready informer**
|
||||
|
||||
1. Tenant создаёт namespace с label → namespace добавляется в NamespaceManager.
|
||||
2. `dispatch()` вызывает router subscriber → `AddNamespace()`:
|
||||
```go
|
||||
k8sCache.WaitForCacheSync(ctx.Done(), triggerInf.HasSynced, funcInf.HasSynced)
|
||||
ts.syncTriggers()
|
||||
```
|
||||
Router ждёт sync и перестраивает роутинг. Это занимает несколько секунд.
|
||||
3. Tenant **немедленно** после создания namespace создаёт HTTPTrigger через API.
|
||||
4. Если trigger создан **до** завершения `WaitForCacheSync` в router → informer ещё не синхронизирован, но trigger уже в etcd.
|
||||
5. После sync informer получит это событие через `AddFunc` → `syncTriggers()`. Это нормально.
|
||||
6. **Проблема в другом**: `dispatch()` вызывает подписчиков **последовательно**. Если executor (первый в списке) занимается `EnsureNamespaceSA` + `registerExecutorTypes` (10–30 сек при медленном API) → router subscriber не вызывается всё это время. HTTPTrigger, созданный в этом окне, попадёт в informer, но router ещё не начал слушать → `AddFunc` для этого trigger не вызовется никогда (resync через 30 мин).
|
||||
7. Результат: trigger существует в etcd, но **отсутствует в роутере 30 минут**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Анализ при 50–100 tenant с churn 10 ns/час
|
||||
|
||||
### Informer Explosion
|
||||
|
||||
При 100 активных tenant:
|
||||
- **Executor (poolmgr)**: 1 `SharedInformerFactory` (Fission CRD) + 1 `SharedInformerFactory` (k8s pods/RS) на NS = 200 factory. Каждая factory запускает горутины на каждый informer (~3–5 горутин). **~600–1000 goroutine** только от poolmgr.
|
||||
- **Executor (newdeploy)**: аналогично — ещё 200 factory, ~600 goroutин.
|
||||
- **Router**: 1 factory на NS = 100 factory, ~200 goroutин.
|
||||
- **buildermgr**: 1 factory на NS = 100 goroutин.
|
||||
|
||||
Итого: **~1500–2000 goroutine** только от informer-ов. При пике churn (10 ns/час) — каждые 6 минут добавляется NS, создаётся ~20 новых горутин, они не убираются при track-only removal.
|
||||
|
||||
При **100 NS × 30 мин resync**: каждые 30 мин каждый informer делает LIST всех объектов в своём NS. 100 × 5 informer-типов × LIST = **500 concurrent LIST-запросов** к Kubernetes API раз в 30 минут — возможный thundering herd.
|
||||
|
||||
### Stuck Failed State
|
||||
|
||||
10 ns/час churn с 1% API error rate = ~2.4 failed namespace/сутки. Каждый остаётся в `failed` навсегда. За 30 дней = ~72 "мёртвых" записи в NamespaceManager. `Snapshot()` возвращает их в `idleObjectReaper` → лишние LIST к k8s API для несуществующих/неактивных NS → ошибки, логи, load.
|
||||
|
||||
### AdoptExistingResources Race
|
||||
|
||||
Каждый рестарт executor-а — race. При rolling update в k8s (новый pod стартует, старый ещё жив): оба executor-а параллельно делают `AdoptExistingResources` → оба патчат `instanceID` на одних и тех же pod-ах → `CleanupOldExecutorObjects` нового экземпляра удаляет pod-ы старого (ожидаемо), но при race может удалить pod, который новый экземпляр уже adoptировал.
|
||||
|
||||
### Router Dedup Gap — критический сценарий при рестарте
|
||||
|
||||
При рестарте executor + router одновременно:
|
||||
1. `FISSION_RESOURCE_NAMESPACES` содержит `fission-fn` (статический NS).
|
||||
2. `namespace.go` `init()` добавляет его в `DefaultNSResolver`.
|
||||
3. `BootstrapAndDispatch()` в NamespaceManager вызывает dispatch для всех managed NS, включая `fission-fn`.
|
||||
4. **Router** `AddNamespace("fission-fn")` → `DefaultNSResolver().AddNamespace("fission-fn")` → **false** (уже добавлен в `init()`!) → **early return, informer для fission-fn НЕ создан**.
|
||||
5. Executor (gpm, newdeploy) — используют own dedup (envLister/deplLister), `fission-fn` там нет → создают informer.
|
||||
6. Router слеп к HTTPTrigger и Function событиям из `fission-fn` при динамическом пути. Спасает только то, что `GetInformersForNamespaces` вызывается в `MakeHTTPTriggerSet` при старте — но только для NS из env.
|
||||
|
||||
**Вывод**: если `fission-fn` включён в `FISSION_RESOURCE_NAMESPACES` И помечен `fission.io/managed=true` — возможна ситуация, когда после рестарта router использует startup-informer, а executor использует watcher-informer с другим lifecycle → рассинхронизация при следующем relabel-цикле.
|
||||
|
||||
---
|
||||
|
||||
## 3. Минимальное изменение: явная state machine без полного рефакторинга
|
||||
|
||||
Текущая проблема: `failed` namespace остаётся в `failed` навсегда — нет retry.
|
||||
|
||||
**Изменение**: добавить reconcile-очередь в `inMemoryNamespaceManager` без изменения публичного интерфейса.
|
||||
|
||||
```go
|
||||
// В inMemoryNamespaceManager добавить:
|
||||
type reconcileRequest struct {
|
||||
ns string
|
||||
attempt int
|
||||
}
|
||||
|
||||
reconcileQueue chan reconcileRequest // небуферизованный или с буфером 64
|
||||
|
||||
// В MarkPartFailed (или в dispatch при возврате ошибки от subscriber):
|
||||
func (m *inMemoryNamespaceManager) enqueueReconcile(ns string, attempt int) {
|
||||
select {
|
||||
case m.reconcileQueue <- reconcileRequest{ns: ns, attempt: attempt}:
|
||||
default: // уже в очереди, skip
|
||||
}
|
||||
}
|
||||
|
||||
// Новая горутина, запускается в BootstrapAndDispatch или отдельным методом:
|
||||
func (m *inMemoryNamespaceManager) RunReconciler(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case req := <-m.reconcileQueue:
|
||||
if req.attempt >= 5 { // max retries
|
||||
m.logger.Error("namespace reconcile exhausted", zap.String("ns", req.ns))
|
||||
continue
|
||||
}
|
||||
backoff := time.Duration(1<<req.attempt) * time.Second // 1, 2, 4, 8, 16 сек
|
||||
time.AfterFunc(backoff, func() {
|
||||
// Повторить dispatch только для failed-частей:
|
||||
m.mu.RLock()
|
||||
rec, ok := m.records[req.ns]
|
||||
m.mu.RUnlock()
|
||||
if !ok || rec.Phase != NamespacePhaseFailed {
|
||||
return // уже исправлено или удалено
|
||||
}
|
||||
// Вызвать только тех подписчиков, у кого часть в FailedState:
|
||||
m.dispatchRetry(ctx, req.ns, req.attempt+1)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Изменения интерфейса**: `NamespaceManager` получает метод `RunReconciler(ctx)` — добавляется в интерфейс, но не breaking change для существующих вызывающих (можно добавить как опциональный метод или вызвать из `BootstrapAndDispatch`).
|
||||
|
||||
**Что НЕ меняется**: `NamespaceSubscriber`, `NamespaceRecord`, публичные методы `Upsert`/`Snapshot`/`Subscribe` — всё прежнее.
|
||||
|
||||
---
|
||||
|
||||
## 4. Track-Only Removal: скрытые допущения и dirty state
|
||||
|
||||
### Допущение 1: `DefaultNSResolver` — только append
|
||||
|
||||
`pkg/utils/namespace.go`: метод `AddNamespace` добавляет NS в глобальный map, метода `RemoveNamespace` не существует. Последствия:
|
||||
|
||||
- Namespace, удалённый через label-снятие, **навсегда остаётся** в глобальном resolver-е.
|
||||
- `idleObjectReaper` в poolmgr и newdeploy делает `DefaultNSResolver().Snapshot()` → итерирует удалённые NS → делает LIST Environments/Functions в уже несуществующем (или чужом) namespace → получает k8s 403/404 → логирует ошибку → возвращает из reaper-а (!) — `return` на ошибке прерывает весь цикл reaper-а для текущей итерации.
|
||||
|
||||
### Допущение 2: Informer-ы продолжают работать
|
||||
|
||||
После track-only removal informer-ы executor-а и router-а **не останавливаются**. Для poolmgr: env-events из удалённого namespace продолжают триггерить создание пулов. Пулы создаются в k8s (или пытаются) — для namespace, который более не является managed. RBAC мог быть уже удалён оператором → pod-ы не могут pull fetcher image → CrashLoopBackOff в "удалённом" namespace.
|
||||
|
||||
### Допущение 3: FunctionServiceCache не очищается
|
||||
|
||||
`fsCache` (в gpm и newdeploy) содержит записи с `Function.Namespace = "tenant-42"`. После track-only removal записи не удаляются. `idleObjectReaper` находит их через `fsCache.ListOldForPool()` → пытается найти pod в `gpm.podLister["tenant-42"]` → lister ещё жив (informer работает) → pod может быть найден → считается "valid" → не reaped → запись в кэше живёт вечно.
|
||||
|
||||
### Допущение 4 (критическое): повторное добавление того же NS → router слеп
|
||||
|
||||
Последовательность:
|
||||
1. NS `tenant-42` добавлен → `DefaultNSResolver().AddNamespace("tenant-42")` → **true** → router создаёт informer.
|
||||
2. NS удалён (track-only) → resolver не очищен → informer router-а продолжает работать.
|
||||
3. NS добавлен снова (новый tenant с тем же именем, например после namespace-переименования).
|
||||
4. `AddNamespace("tenant-42")` на router-е → `DefaultNSResolver().AddNamespace("tenant-42")` → **false** (уже в map!) → **early return**.
|
||||
5. Router **не создаёт новый informer** — считает что уже обслуживает namespace. Но старый informer работает с **кэшем от предыдущего tenants** — старые Function и HTTPTrigger объекты (с другими UID) видны в `funcInformer.GetStore()`.
|
||||
6. Executor (gpm): `poolPodC.envLister["tenant-42"]` тоже существует → own dedup → early return → executor тоже не создаёт новый informer.
|
||||
7. Новые HTTPTrigger-ы нового tenant-42 **никогда не попадут в router** (resync через 30 мин принесёт их, но с кэшем старого tenanta!).
|
||||
|
||||
**Результат**: dirty state — оба компонента убеждены что всё нормально, но фактически обслуживают кэш несуществующего tenant с объектами с устаревшими UID. Вызовы функций нового tenant → 404 или выполнение **функций старого tenant** если имена совпадают.
|
||||
|
||||
---
|
||||
|
||||
## 5. Оценка замены centralized mutex на sharded lock
|
||||
|
||||
### Техническая реализация (feasible)
|
||||
|
||||
```go
|
||||
const numShards = 16
|
||||
|
||||
type shardedNamespaceManager struct {
|
||||
shards [numShards]nsShard
|
||||
subsMu sync.RWMutex
|
||||
subs map[string]NamespaceSubscriber
|
||||
// ... остальные поля
|
||||
}
|
||||
|
||||
type nsShard struct {
|
||||
mu sync.RWMutex
|
||||
records map[string]NamespaceRecord // только NS принадлежащие этому шарду
|
||||
}
|
||||
|
||||
func shardIndex(ns string) int {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(ns))
|
||||
return int(h.Sum32()) % numShards
|
||||
}
|
||||
```
|
||||
|
||||
`Upsert(ns, ...)` → берёт lock только шарда `shardIndex(ns)`.
|
||||
`Get(ns)` → RLock только нужного шарда.
|
||||
`Snapshot()` → **последовательно** берёт RLock каждого шарда, копирует, освобождает, переходит к следующему. N=16 последовательных lock-acquisitions.
|
||||
|
||||
### Сохранение интерфейса
|
||||
|
||||
Публичный интерфейс `NamespaceManager` (Upsert, Get, Snapshot, Subscribe, Dispatch) не меняется. Подписчики (`NamespaceSubscriber`) не меняются.
|
||||
|
||||
### Анализ выгоды
|
||||
|
||||
При 10 ns/час churn: **одно upsert каждые 6 минут**. Текущий bottleneck — не mutex, а:
|
||||
1. Synchronous subscriber dispatch (каждый делает k8s API calls)
|
||||
2. Informer resync thundering herd
|
||||
3. AdoptExistingResources race
|
||||
|
||||
Sharded lock убирает конкуренцию за mutex при **параллельных per-namespace операциях**. Но `dispatch()` сам снимает/берёт lock несколько раз — sharding не помогает здесь (dispatch по одному NS всегда один шард).
|
||||
|
||||
`Snapshot()` становится чуть медленнее (16 lock-acquisitions вместо 1 RLock) при маленьком числе NS, и сопоставима при большом.
|
||||
|
||||
### Вердикт
|
||||
|
||||
**Технически реализуемо с сохранением интерфейса. Не оправдано при текущей нагрузке.**
|
||||
|
||||
Sharded mutex даст реальный выигрыш только если `Upsert` и `Get` вызываются **параллельно для разных NS** с частотой > 100 ops/sec. При 10 ns/час это недостижимо. Реальные bottleneck-и — в subscriber dispatch и informer lifecycle, не в mutex.
|
||||
|
||||
Приоритет вместо sharding:
|
||||
1. Сделать subscriber dispatch **параллельным** (goroutine per subscriber с errgroup) — немедленное ускорение онбординга.
|
||||
2. Добавить `RemoveNamespace` в `DefaultNSResolver` — закрывает класс dirty-state багов.
|
||||
3. Добавить reconcile-очередь (см. п. 3) — закрывает stuck-failed.
|
||||
|
||||
Sharded lock — в backlog, актуально при > 500 concurrent tenant с > 1 onboarding/sec.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Интеграционный тест: P1/P2 (namespace lifecycle hardening)
|
||||
|
||||
## Контекст
|
||||
Ветка: `fix/namespace-lifecycle-hardening` (fission-src)
|
||||
Коммиты: P1 (auto-recovery failed NS), P2 (adopt orphaned pods)
|
||||
Образ executor: `naeel/fission-bundle:v1.22.1` (задеплоен 2026-05-18)
|
||||
Тестирование — через **fission-console** UI.
|
||||
|
||||
---
|
||||
|
||||
## 1. Проверка P1: auto-recovery failed NS
|
||||
|
||||
1. Открыть fission-console, создать tenant (новый managed namespace)
|
||||
2. Нарочно сломать ServiceAccount или RoleBinding:
|
||||
```bash
|
||||
kubectl delete sa fission-fetcher -n <tenant-ns>
|
||||
```
|
||||
3. В UI убедиться, что namespace ушёл в фазу `failed`
|
||||
4. Через ~30 сек namespace должен автоматически восстановиться (`active`)
|
||||
- SA/RB пересозданы
|
||||
- Функции снова работают (запустить любую тестовую функцию)
|
||||
|
||||
---
|
||||
|
||||
## 2. Проверка P2: adopt orphaned pods после рестарта executor
|
||||
|
||||
1. Через fission-console вызвать несколько функций (чтобы были warm pods)
|
||||
2. Рестартовать executor:
|
||||
```bash
|
||||
kubectl rollout restart deployment/executor -n fission
|
||||
kubectl rollout status deployment/executor -n fission
|
||||
```
|
||||
3. Убедиться что:
|
||||
- Функции продолжают работать без cold start задержки
|
||||
- В логах executor есть строки `PreRegisterManagedNamespaces`, `adopt`, `cleanup`:
|
||||
```bash
|
||||
kubectl logs -n fission -l svc=executor --tail=100 | grep -E "PreRegister|adopt|cleanup"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Проверить отсутствие регрессий (через fission-console)
|
||||
|
||||
- Создание/удаление tenant работает
|
||||
- Функции создаются, редактируются, выполняются
|
||||
- Логи функций доступны в UI
|
||||
- Нет ошибок в UI и в логах executor/router
|
||||
|
||||
---
|
||||
|
||||
## 4. Команды для диагностики
|
||||
|
||||
```bash
|
||||
# Текущий образ executor
|
||||
kubectl get deployment executor -n fission -o jsonpath="{.spec.template.spec.containers[0].image}"
|
||||
|
||||
# Логи executor (последние 200 строк)
|
||||
kubectl logs -n fission -l svc=executor --tail=200
|
||||
|
||||
# Статус managed NS
|
||||
kubectl get ns -l managed-by=fission
|
||||
```
|
||||
@@ -0,0 +1,644 @@
|
||||
# Fission Console API — Руководство пользователя
|
||||
|
||||
> Версия: актуальна для модернизированного Fission с мультитенантностью (ngcloud).
|
||||
|
||||
---
|
||||
|
||||
## Базовый URL
|
||||
|
||||
```
|
||||
https://fission.kube5s.ru/console/api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Аутентификация
|
||||
|
||||
### Где взять токен
|
||||
|
||||
Сервер поддерживает два типа токенов — определяет автоматически по форме:
|
||||
|
||||
| Форма токена | Тип | Описание |
|
||||
|---|---|---|
|
||||
| JWT (три части через `.`) | **Production** | JWT из личного кабинета NUBES (Профиль → Токены). Валидируется через Deck API облака |
|
||||
| Любая строка ≥ 6 символов | **Demo** | Любой произвольный логин — без внешней проверки. Удобно для разработки и тестирования |
|
||||
| Строка < 6 символов | — | 401 |
|
||||
|
||||
**Production (NUBES):** JWT-токен берётся в личном кабинете NUBES → Профиль → Токены.
|
||||
**Demo:** любая строка ≥ 6 символов — например `myuser@example.com` или `dev-user-1`.
|
||||
|
||||
### Передача токена
|
||||
|
||||
Два способа — оба равнозначны:
|
||||
|
||||
```http
|
||||
X-Auth-Token: <токен>
|
||||
```
|
||||
```http
|
||||
Authorization: Bearer <токен>
|
||||
```
|
||||
|
||||
### POST /auth
|
||||
|
||||
Проверка токена и получение информации о своём namespace.
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/auth \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"token": "myuser@example.com", "env": "test"}'
|
||||
```
|
||||
|
||||
**Параметры:**
|
||||
| Поле | Описание |
|
||||
|---|---|
|
||||
| `token` | Токен (JWT или demo-строка) |
|
||||
| `env` | Стенд: `prod`, `dev`, `test` (только для JWT; по умолчанию `test`) |
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"env": "test",
|
||||
"namespace": "fission-a3f9c1b2d4e6f8a1",
|
||||
"email": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 400 | Тело не JSON или `token` пустой |
|
||||
| 401 | Токен < 6 символов или JWT не прошёл валидацию в Deck API |
|
||||
| 405 | GET вместо POST |
|
||||
|
||||
> **Namespace детерминирован**: `fission-` + hex(SHA256(sub)[:8]) — одинаковый токен → всегда один namespace.
|
||||
> Namespace и RBAC создаются автоматически при первом обращении.
|
||||
|
||||
---
|
||||
|
||||
## Мультитенантность ★ КЛЮЧЕВОЕ ОТЛИЧИЕ
|
||||
|
||||
- Каждый пользователь работает в **изолированном K8s namespace**: `fission-<hash(token)>`
|
||||
- Все операции (создание, список, вызов, удаление) **автоматически ограничены своим namespace**
|
||||
- Указать namespace вручную **невозможно**
|
||||
- Функции другого пользователя **не видны и не доступны** — любая операция над чужим объектом возвращает **404** (не 403, чтобы не раскрывать факт существования)
|
||||
- **Routes изолированы**: функции разных пользователей с одинаковым именем получают разные HTTP-маршруты
|
||||
|
||||
### Квоты (применяются автоматически, значения по умолчанию)
|
||||
|
||||
| Ресурс | Лимит |
|
||||
|--------|-------|
|
||||
| Функции (`count/functions.fission.io`) | 20 |
|
||||
| Пакеты (`count/packages.fission.io`) | 40 |
|
||||
| HTTP Triggers (`count/httptriggers.fission.io`) | 20 |
|
||||
| Pods | 30 |
|
||||
| CPU requests (суммарно) | 1 |
|
||||
| CPU limits (суммарно) | 12 |
|
||||
| RAM requests (суммарно) | 2 Gi |
|
||||
| RAM limits (суммарно) | 6 Gi |
|
||||
|
||||
> Значения настраиваются env vars (`QUOTA_REQ_CPU`, `QUOTA_PODS`, и т.д.) без пересборки.
|
||||
|
||||
---
|
||||
|
||||
## Функции
|
||||
|
||||
### POST /functions — Создать функцию из кода (JSON)
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/functions \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "my-fn",
|
||||
"language": "nodejs",
|
||||
"code": "module.exports = async function(ctx) { return { status: 200, body: \"hello\" }; }"
|
||||
}'
|
||||
```
|
||||
|
||||
**Параметры запроса:**
|
||||
| Поле | Тип | Обязательно | Описание |
|
||||
|------|-----|:-----------:|---------|
|
||||
| `name` | string | ✓ | Имя функции (см. правила ниже) |
|
||||
| `language` | string | ✓ | Среда выполнения: `nodejs`, `python`, `go`, `php`, `ruby` |
|
||||
| `code` | string | ✓ | Исходный код (строка). Максимум 1 MB |
|
||||
| `entrypoint` | string | — | Точка входа (по умолчанию — зависит от языка) |
|
||||
| `route` | string | — | HTTP-маршрут (по умолчанию `/<ns-suffix>/<name>`) |
|
||||
| `methods` | []string | — | HTTP-методы (по умолчанию `["GET"]`) |
|
||||
| `timeout` | int64 | — | Таймаут функции в секундах |
|
||||
| `ttl` | string | — | Время жизни функции: `15m`, `1h`, `2d` и т.д. ★ |
|
||||
|
||||
**Правила именования (`name`):**
|
||||
- Только строчные буквы, цифры, дефис
|
||||
- Не начинается и не заканчивается дефисом
|
||||
- Максимум **57 символов**
|
||||
|
||||
**Ответ 201:**
|
||||
```json
|
||||
{
|
||||
"name": "my-fn",
|
||||
"package": "my-fn-pkg",
|
||||
"httptrigger": "my-fn-route",
|
||||
"route": "/a3f9c1b2d4e6/my-fn",
|
||||
"expires_at": null
|
||||
}
|
||||
```
|
||||
> `expires_at` — время удаления функции (RFC3339), `null` если TTL не задан.
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 400 | Нет `name`/`language`/`code`, невалидное имя, неизвестный язык, код > 1 MB, невалидный TTL |
|
||||
| 409 | Функция с таким именем уже существует у этого пользователя |
|
||||
|
||||
---
|
||||
|
||||
### POST /functions — Создать функцию из zip-архива (multipart)
|
||||
|
||||
Альтернативный способ: передать архив напрямую при создании функции.
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/functions \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-F "name=my-fn" \
|
||||
-F "language=python" \
|
||||
-F "entrypoint=main.handler" \
|
||||
-F "archive=@my-function.zip"
|
||||
```
|
||||
|
||||
**Параметры формы (multipart/form-data):**
|
||||
| Поле | Тип | Обязательно | Описание |
|
||||
|------|-----|:-----------:|---------|
|
||||
| `name` | string | ✓ | Имя функции |
|
||||
| `language` | string | ✓* | Язык (`python`, `nodejs`, `go`, `php`, `ruby`) — или `environment` |
|
||||
| `environment` | string | ✓* | Явное имя environment (вместо `language`) |
|
||||
| `archive` | file | ✓ | zip-архив с кодом. Максимум 100 KB |
|
||||
| `entrypoint` | string | — | Точка входа |
|
||||
| `route` | string | — | HTTP-маршрут |
|
||||
| `methods` | string | — | HTTP-методы через запятую (`GET,POST`) |
|
||||
| `timeout` | string | — | Таймаут в секундах |
|
||||
| `ttl` | string | — | Время жизни: `15m`, `1h`, `2d` и т.д. |
|
||||
|
||||
> Архив должен быть валидным zip (magic bytes `PK`). Максимальный суммарный распакованный размер — 100 KB (защита от zip bomb).
|
||||
|
||||
**Ответ 201:**
|
||||
```json
|
||||
{
|
||||
"name": "my-fn",
|
||||
"namespace": "fission-a3f9c1b2d4e6f8a1",
|
||||
"environment": "python-env",
|
||||
"route": "/a3f9c1b2d4e6/my-fn",
|
||||
"source_type": "archive"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /functions — Список функций
|
||||
|
||||
```bash
|
||||
curl https://fission.kube5s.ru/console/api/functions \
|
||||
-H "X-Auth-Token: user@domain.com"
|
||||
```
|
||||
|
||||
**Ответ 200** — массив сырых K8s объектов типа `Function`. Новый пользователь → `[]`.
|
||||
Возвращает **только функции текущего пользователя**.
|
||||
|
||||
---
|
||||
|
||||
### GET /functions/{name} — Описание функции
|
||||
|
||||
```bash
|
||||
curl https://fission.kube5s.ru/console/api/functions/my-fn \
|
||||
-H "X-Auth-Token: user@domain.com"
|
||||
```
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"name": "my-fn",
|
||||
"namespace": "fission-a3f9c1b2d4e6f8a1",
|
||||
"environment": "nodejs-env",
|
||||
"package": "my-fn-pkg",
|
||||
"entrypoint": "main",
|
||||
"timeout": 60,
|
||||
"created_at": "2026-05-01T10:00:00Z",
|
||||
"updated_at": "2026-05-01T12:00:00Z",
|
||||
"code": "module.exports = async function(ctx) { ... }",
|
||||
"source_type": "code",
|
||||
"archive_filename": "",
|
||||
"route": "/a3f9c1b2d4e6/my-fn",
|
||||
"methods": ["GET", "POST"],
|
||||
"raw": {}
|
||||
}
|
||||
```
|
||||
|
||||
> `code` — исходный код (если хранится как literal). Для функций из архива может быть пустым.
|
||||
> `source_type` — `"code"` или `"archive"`.
|
||||
> `raw` — полный K8s объект Function.
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 404 | Функция не существует или принадлежит другому пользователю |
|
||||
|
||||
---
|
||||
|
||||
### POST /functions/{name}/invoke — Вызов функции
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/functions/my-fn/invoke \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"latency_ms": 42,
|
||||
"response_raw": "hello"
|
||||
}
|
||||
```
|
||||
|
||||
> `response_raw` — тело ответа функции как строка.
|
||||
> `status` — HTTP-статус ответа функции.
|
||||
> `latency_ms` — время выполнения в миллисекундах.
|
||||
> Cold start (первый вызов после создания) может занять **10-60 секунд** — Pod создаётся и прогревается. Последующие вызовы быстрые.
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 404 | Функция не существует или принадлежит другому пользователю |
|
||||
| 502 | Fission router недоступен или функция завершилась с timeout |
|
||||
|
||||
---
|
||||
|
||||
### PUT /functions/{name}/code — Обновить код функции ★
|
||||
|
||||
Обновляет код существующей функции. Создаётся новый Package, executor подхватывает его при следующем вызове.
|
||||
|
||||
```bash
|
||||
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/code \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"code": "module.exports = async function(ctx) { return { status: 200, body: \"v2\" }; }"
|
||||
}'
|
||||
```
|
||||
|
||||
**Параметры:**
|
||||
| Поле | Тип | Обязательно | Описание |
|
||||
|------|-----|:-----------:|---------|
|
||||
| `code` | string | ✓ | Новый исходный код |
|
||||
| `timeout` | int64 | — | Новый таймаут в секундах |
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"updated": true,
|
||||
"package": "my-fn-pkg-xxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 400 | `code` пустой или только пробелы |
|
||||
| 404 | Функция не существует или принадлежит другому пользователю |
|
||||
|
||||
---
|
||||
|
||||
### PUT /functions/{name}/archive — Обновить архив функции ★
|
||||
|
||||
Обновляет функцию новым zip-архивом (multipart/form-data, поле `archive`).
|
||||
|
||||
```bash
|
||||
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/archive \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-F "archive=@my-function-v2.zip"
|
||||
```
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"updated": true,
|
||||
"package": "my-fn-pkg-xxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PUT /functions/{name}/timeout — Обновить таймаут функции ★
|
||||
|
||||
Обновляет только таймаут (и опционально entrypoint) без замены кода или архива.
|
||||
|
||||
```bash
|
||||
curl -X PUT https://fission.kube5s.ru/console/api/functions/my-fn/timeout \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"timeout": 120}'
|
||||
```
|
||||
|
||||
**Параметры:**
|
||||
| Поле | Тип | Описание |
|
||||
|------|-----|---------|
|
||||
| `timeout` | int64 | Новый таймаут в секундах |
|
||||
| `entrypoint` | string | Новая точка входа (опционально) |
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"updated": true,
|
||||
"timeout": 120
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DELETE /functions/{name} — Удалить функцию
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://fission.kube5s.ru/console/api/functions/my-fn \
|
||||
-H "X-Auth-Token: user@domain.com"
|
||||
```
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"deleted": true,
|
||||
"name": "my-fn",
|
||||
"package": "my-fn-pkg"
|
||||
}
|
||||
```
|
||||
|
||||
> Удаляются также связанные HTTPTrigger, TimeTrigger и Package.
|
||||
> Архив в S3 удаляется асинхронно.
|
||||
> Если язык больше не используется ни одной функцией — environment Pod'ы убираются автоматически.
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 404 | Функция не существует или принадлежит другому пользователю |
|
||||
|
||||
> Повторное удаление той же функции → **404**.
|
||||
|
||||
---
|
||||
|
||||
## Прямой вызов по route — GET|POST /fn/{route}
|
||||
|
||||
Вызов функции напрямую по HTTP-маршруту без обёртки invoke. Ответ проксируется как есть — без JSON-обёртки.
|
||||
|
||||
```bash
|
||||
curl https://fission.kube5s.ru/fn/a3f9c1b2d4e6/my-fn \
|
||||
-H "X-Auth-Token: user@domain.com"
|
||||
```
|
||||
|
||||
> Используйте этот endpoint когда нужно получить чистый HTTP-ответ функции, а не JSON-обёртку с `response_raw`.
|
||||
> Метод запроса (GET/POST/…) проксируется без изменений.
|
||||
|
||||
---
|
||||
|
||||
## Time Triggers (расписание)
|
||||
|
||||
### GET /timetriggers — Список
|
||||
|
||||
```bash
|
||||
curl https://fission.kube5s.ru/console/api/timetriggers \
|
||||
-H "X-Auth-Token: user@domain.com"
|
||||
```
|
||||
|
||||
Возвращает массив сырых K8s объектов TimeTrigger.
|
||||
|
||||
### POST /timetriggers — Создать
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/timetriggers \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "my-cron",
|
||||
"functionName": "my-fn",
|
||||
"cron": "*/5 * * * *"
|
||||
}'
|
||||
```
|
||||
|
||||
**Параметры:**
|
||||
| Поле | Тип | Обязательно | Описание |
|
||||
|------|-----|:-----------:|---------|
|
||||
| `name` | string | ✓ | Имя trigger'а |
|
||||
| `functionName` | string | ✓ | Имя функции |
|
||||
| `cron` | string | ✓ | Cron-выражение (стандартный формат) |
|
||||
| `method` | string | — | HTTP-метод для вызова (по умолчанию `POST`) |
|
||||
| `subpath` | string | — | Дополнительный путь |
|
||||
|
||||
**Ответ 201:**
|
||||
```json
|
||||
{
|
||||
"name": "my-cron",
|
||||
"namespace": "fission-a3f9c1b2d4e6f8a1",
|
||||
"cron": "*/5 * * * *",
|
||||
"method": "POST",
|
||||
"subpath": "",
|
||||
"function": "my-fn",
|
||||
"raw": {}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /timetriggers/{name} — Описание
|
||||
|
||||
**Ответ 200** — та же структура что и при создании.
|
||||
|
||||
### PUT /timetriggers/{name} — Обновить
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"updated": true,
|
||||
"trigger": { ...та же структура... }
|
||||
}
|
||||
```
|
||||
|
||||
### DELETE /timetriggers/{name} — Удалить
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"deleted": true,
|
||||
"name": "my-cron"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI-линтер архивов ★
|
||||
|
||||
Проверяет zip-архив на синтаксические ошибки до деплоя. Не создаёт функцию.
|
||||
Поддерживаемые файлы: `.py`, `.js`, `.rb`, `.php`.
|
||||
|
||||
### POST /ai/lint-archive
|
||||
|
||||
```bash
|
||||
curl -X POST https://fission.kube5s.ru/console/api/ai/lint-archive \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-F "archive=@my-function.zip" \
|
||||
-F "entrypoint=main.handler" \
|
||||
-F "language=python"
|
||||
```
|
||||
|
||||
**Параметры формы:**
|
||||
| Поле | Описание |
|
||||
|------|---------|
|
||||
| `archive` | zip-архив (обязательно) |
|
||||
| `entrypoint` | Точка входа `module.function` — проверяется что файл и функция существуют в архиве |
|
||||
| `language` | Язык — проверяется что архив содержит файлы нужного расширения |
|
||||
|
||||
**Ответ 200:**
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"results": [
|
||||
{"file": "main.py", "ok": true},
|
||||
{"file": "helper.py", "ok": false, "output": "SyntaxError: invalid syntax (helper.py, line 5)"},
|
||||
{"file": "main.handler", "ok": true, "output": "entrypoint 'main.handler' найден"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> `ok: false` в корне объекта означает что хотя бы один файл не прошёл проверку.
|
||||
> `output` содержит вывод линтера — присутствует только при ошибке (для entrypoint — всегда).
|
||||
|
||||
**Ошибки:**
|
||||
| Код | Причина |
|
||||
|-----|---------|
|
||||
| 400 | Нет поля `archive`, нет поддерживаемых файлов (.py/.js/.rb/.php) в архиве |
|
||||
| 413 | Архив > 100 KB или суммарный распакованный размер > 100 KB (zip bomb protection) |
|
||||
|
||||
---
|
||||
|
||||
## TTL — Время жизни функции ★
|
||||
|
||||
Функция может быть создана с ограниченным временем жизни. После истечения TTL функция удаляется автоматически.
|
||||
|
||||
**Формат:** число + суффикс: `m` (минуты), `h` (часы), `d` (дни).
|
||||
**Примеры:** `15m`, `1h`, `2d`, `12h`
|
||||
|
||||
```bash
|
||||
curl -X POST .../functions \
|
||||
-H "X-Auth-Token: user@domain.com" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "temp-fn",
|
||||
"language": "python",
|
||||
"code": "def main(event, context): return \"hi\"",
|
||||
"ttl": "1h"
|
||||
}'
|
||||
```
|
||||
|
||||
В ответе будет поле `expires_at` (формат RFC3339):
|
||||
```json
|
||||
{
|
||||
"name": "temp-fn",
|
||||
"package": "temp-fn-pkg",
|
||||
"httptrigger": "temp-fn-route",
|
||||
"route": "/...",
|
||||
"expires_at": "2026-05-06T14:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
Невалидные значения TTL (`0d`, `-1h`, `99z`, `abc`) → **400**.
|
||||
|
||||
---
|
||||
|
||||
## HTTP-коды — сводная таблица
|
||||
|
||||
| Код | Значение |
|
||||
|-----|---------|
|
||||
| 200 | Успех (GET, DELETE, PUT) |
|
||||
| 201 | Объект создан (POST /functions, POST /timetriggers) |
|
||||
| 400 | Ошибка валидации параметров |
|
||||
| 401 | Не авторизован (нет токена или < 6 символов) |
|
||||
| 404 | Объект не найден (или чужой) |
|
||||
| 405 | Неверный HTTP-метод |
|
||||
| 409 | Конфликт (дубликат имени) |
|
||||
| 413 | Тело слишком большое (код > 1 MB, архив > 100 KB) |
|
||||
| 502 | Ошибка взаимодействия с Fission (router/executor недоступен) |
|
||||
|
||||
**Формат ошибки:**
|
||||
```json
|
||||
{ "error": "описание ошибки" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Поддерживаемые языки
|
||||
|
||||
| `language` | Расширение файла | Entrypoint по умолчанию |
|
||||
|------------|-----------------|------------------------|
|
||||
| `python` | `.py` | `main.main` |
|
||||
| `nodejs` | `.js` | зависит от runtime |
|
||||
| `go` | `.go` | зависит от runtime |
|
||||
| `php` | `.php` | зависит от runtime |
|
||||
| `ruby` | `.rb` | зависит от runtime |
|
||||
|
||||
---
|
||||
|
||||
## Примеры сценариев
|
||||
|
||||
### Быстрый старт (inline-код)
|
||||
```bash
|
||||
BASE="https://fission.kube5s.ru/console/api"
|
||||
TOKEN="myuser@example.com"
|
||||
|
||||
# Создать функцию
|
||||
curl -X POST "$BASE/functions" \
|
||||
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"name":"hello","language":"python","code":"def main(event, context): return \"hello world\""}'
|
||||
|
||||
# Вызвать
|
||||
curl -X POST "$BASE/functions/hello/invoke" \
|
||||
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" -d '{}'
|
||||
|
||||
# Обновить код
|
||||
curl -X PUT "$BASE/functions/hello/code" \
|
||||
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"code":"def main(event, context): return \"v2\""}'
|
||||
|
||||
# Удалить
|
||||
curl -X DELETE "$BASE/functions/hello" -H "X-Auth-Token: $TOKEN"
|
||||
```
|
||||
|
||||
### Создать из архива напрямую
|
||||
```bash
|
||||
curl -X POST "$BASE/functions" \
|
||||
-H "X-Auth-Token: $TOKEN" \
|
||||
-F "name=my-fn" \
|
||||
-F "language=python" \
|
||||
-F "entrypoint=main.handler" \
|
||||
-F "archive=@my-fn.zip"
|
||||
```
|
||||
|
||||
### Проверить архив перед деплоем
|
||||
```bash
|
||||
curl -X POST "$BASE/ai/lint-archive" \
|
||||
-H "X-Auth-Token: $TOKEN" \
|
||||
-F "archive=@my-fn.zip" \
|
||||
-F "entrypoint=main.handler" \
|
||||
-F "language=python"
|
||||
```
|
||||
|
||||
### Создать временную функцию (исчезнет через 30 минут)
|
||||
```bash
|
||||
curl -X POST "$BASE/functions" \
|
||||
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"name":"temp-fn","language":"nodejs","code":"module.exports = async () => ({status:200,body:\"tmp\"})","ttl":"30m"}'
|
||||
```
|
||||
|
||||
### Настроить расписание
|
||||
```bash
|
||||
# Вызывать my-fn каждые 5 минут
|
||||
curl -X POST "$BASE/timetriggers" \
|
||||
-H "X-Auth-Token: $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"name":"my-cron","functionName":"my-fn","cron":"*/5 * * * *"}'
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
# Console ↔ fission-src multitenant: compatibility check (2026-05-15)
|
||||
|
||||
## Что изменилось в fission-src (feature/multitenant)
|
||||
|
||||
| Изменение | Файл |
|
||||
|---|---|
|
||||
| Удалён старый partial RBAC | `deploy/executor-ns-watcher-rbac.yaml` |
|
||||
| Добавлен полный RBAC для NSWatcher | `deploy/multitenant/rbac.yaml` |
|
||||
| Добавлен `EnsureNamespaceSA` | `pkg/utils/serviceaccount.go` |
|
||||
| NSWatcher вызывает `EnsureNamespaceSA` при обнаружении NS с `fission.io/managed=true` | `pkg/executor/multitenant/ns_watcher.go` |
|
||||
| Добавлен тест NSWatcher с fake k8s | `pkg/utils/namespace_manager_test.go` |
|
||||
|
||||
---
|
||||
|
||||
## Что делает консоль при создании namespace
|
||||
|
||||
`SetupFissionNamespace` в `console/internal/fission/namespace.go`:
|
||||
|
||||
1. Создаёт Namespace с лейблами:
|
||||
- `managed-by=fission-console`
|
||||
- `fission.io/managed=true` ← триггер для NSWatcher
|
||||
|
||||
2. Создаёт ServiceAccounts: `fission-fetcher`, `fission-builder`
|
||||
|
||||
3. Создаёт RoleBindings с `cluster-admin` ClusterRole для всех Fission SA:
|
||||
- `fission-executor`, `fission-router`, `fission-buildermgr`, `fission-kubewatcher`, `fission-timer`
|
||||
- `fission-fetcher` (из fission NS + локально в user NS)
|
||||
- `fission-builder` (из fission NS + локально в user NS)
|
||||
|
||||
---
|
||||
|
||||
## Взаимодействие с EnsureNamespaceSA
|
||||
|
||||
`EnsureNamespaceSA` вызывается NSWatcher **после** того как консоль создала NS.
|
||||
Логика (в `setupSAAndRoleBindings`):
|
||||
|
||||
1. Создаёт/получает SA `fission-fetcher` → SA уже существует → `IsAlreadyExists` → OK
|
||||
2. Для каждого permission из `fetcherCheck` вызывает `checkPermission` через `localsubjectaccessreviews`
|
||||
3. Поскольку у `fission-fetcher` уже есть `cluster-admin` RoleBinding (создан консолью) →
|
||||
**все проверки возвращают `exists=true`** → `rules` остаётся пустым → Role и RoleBinding **не создаются**
|
||||
|
||||
**Итог: EnsureNamespaceSA является no-op если консоль уже настроила namespace. Никаких конфликтов.**
|
||||
|
||||
---
|
||||
|
||||
## Что нужно на кластере
|
||||
|
||||
Для работы NSWatcher нужен `deploy/multitenant/rbac.yaml` применён **один раз**:
|
||||
```bash
|
||||
kubectl apply -f ~/terra/fission-src/deploy/multitenant/rbac.yaml
|
||||
```
|
||||
|
||||
Это даёт:
|
||||
- `fission-executor` → `list/watch namespaces` (NSWatcher)
|
||||
- `fission-router` → `list/watch namespaces` (NSWatcher)
|
||||
- `fission-executor` → `create SA/Role/RoleBinding` в user NS (`fission-executor-sa-provisioner`)
|
||||
|
||||
Без этого RBAC `EnsureNamespaceSA` будет падать с Forbidden, но **консоль продолжит работать** — она создаёт SA/RoleBindings сама и не зависит от NSWatcher.
|
||||
|
||||
---
|
||||
|
||||
## Вердикт
|
||||
|
||||
| Сценарий | Статус |
|
||||
|---|---|
|
||||
| Новый NS создаётся через консоль | ✅ работает как раньше |
|
||||
| NSWatcher обнаруживает NS по `fission.io/managed=true` | ✅ совместимо |
|
||||
| `EnsureNamespaceSA` вызывается в уже настроенном NS | ✅ no-op, нет конфликтов |
|
||||
| Старый NS (без нового fission-bundle) | ✅ консоль не зависит от NSWatcher |
|
||||
| Сборка консоли (`go build ./...`) | ✅ BUILD OK |
|
||||
|
||||
**Код консоли менять не нужно.** Нужно только применить `deploy/multitenant/rbac.yaml` при деплое нового fission-bundle.
|
||||
@@ -0,0 +1,349 @@
|
||||
# Fission Multi-Tenant Porting Guide
|
||||
|
||||
> **Purpose:** This document exists so that any AI agent or engineer can fully
|
||||
> understand what was changed to add multi-tenancy to this Fission fork, why
|
||||
> each decision was made, and what needs to be ported when a new upstream
|
||||
> Fission release arrives.
|
||||
>
|
||||
> Fork base: `github.com/fission/fission` tag `v1.22.0` (2025-12-16)
|
||||
> Our branch: `feature/multitenant`
|
||||
|
||||
---
|
||||
|
||||
## 1. The Problem We Solved
|
||||
|
||||
In stock Fission v1.22.0 all resource namespaces must be listed in the
|
||||
`FISSION_RESOURCE_NAMESPACES` environment variable **before** the process starts.
|
||||
Adding a new tenant namespace requires:
|
||||
|
||||
1. Patching that env var on executor, router, buildermgr deployments
|
||||
2. Triggering a rolling restart of all three components (~30 s downtime each)
|
||||
|
||||
At scale (hundreds of tenants created continuously) this causes a permanent
|
||||
rolling-restart loop and cascading failures for existing users.
|
||||
|
||||
**Our solution:** hot namespace registration without pod restarts. Any platform
|
||||
(console, operator, CI/CD) creates a Kubernetes Namespace with label
|
||||
`fission.io/managed=true` — all three Fission components detect it within
|
||||
milliseconds via k8s Watch and register it live.
|
||||
|
||||
---
|
||||
|
||||
## 2. Integration Contract (external platforms)
|
||||
|
||||
The entire contract between an external platform and Fission is a single label:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: tenant-abc123
|
||||
labels:
|
||||
fission.io/managed: "true"
|
||||
```
|
||||
|
||||
No other coupling to Fission internals is required.
|
||||
|
||||
To remove a tenant namespace: delete the namespace or remove the label.
|
||||
Current removal strategy is `track-only` (the manager records the event but does
|
||||
not actively deregister — the executor types stop receiving events for deleted
|
||||
resources naturally). `dispatch-remove` strategy exists in the model but is not
|
||||
wired by default (see §8).
|
||||
|
||||
---
|
||||
|
||||
## 2a. How the Console (`../fission`) Integrates
|
||||
|
||||
The Fission Console (`github.com/naeel/fission`, package `console`) creates tenant
|
||||
namespaces via `SetupFissionNamespace()` in
|
||||
`console/internal/fission/namespace.go`.
|
||||
|
||||
That function does three things:
|
||||
1. Creates the Namespace with two labels:
|
||||
- `managed-by=fission-console` — console's own filter
|
||||
- **`fission.io/managed=true`** — this is the NSWatcher trigger
|
||||
2. Creates `fission-fetcher` and `fission-builder` ServiceAccounts in the new NS
|
||||
3. Creates RoleBindings for all Fission system SAs (`fission-executor`,
|
||||
`fission-router`, `fission-buildermgr`, etc.) using `cluster-admin` scoped to
|
||||
the namespace
|
||||
|
||||
**The coupling is exactly one label.** The console does not call any Fission
|
||||
internal API to register the namespace — it just sets `fission.io/managed=true`
|
||||
and the NSWatcher in executor/router/buildermgr picks it up automatically within
|
||||
~50ms.
|
||||
|
||||
**Before `v1.22.0-mt1` (today's deploy):** the console set the label but the
|
||||
executor was running the official `ghcr.io/fission/fission-bundle:v1.22.0` image
|
||||
which has no NSWatcher — so the label was silently ignored. Tenant namespaces
|
||||
still worked because the console also created the SA/RoleBindings manually (step 2
|
||||
and 3 above), so pool pods could start. But executor/router/buildermgr were not
|
||||
dynamically aware of new namespaces — they relied on whatever was in
|
||||
`FISSION_RESOURCE_NAMESPACES` at startup.
|
||||
|
||||
**After `v1.22.0-mt1`:** executor/router/buildermgr detect the label
|
||||
automatically. The SA creation in `EnsureNamespaceSA` (our code in
|
||||
`pkg/utils/serviceaccount.go`) now runs from the executor side as well — but since
|
||||
the console already created the SA, `EnsureNamespaceSA` is a no-op (idempotent).
|
||||
No conflict, no double work.
|
||||
|
||||
---
|
||||
|
||||
## 3. Backward Compatibility
|
||||
|
||||
`FISSION_RESOURCE_NAMESPACES` continues to work exactly as before. Namespaces
|
||||
listed there are bootstrapped at startup with source `env` and do not require the
|
||||
label. The NSWatcher layer adds **on top** of the existing mechanism — nothing
|
||||
was removed.
|
||||
|
||||
---
|
||||
|
||||
## 4. File Map
|
||||
|
||||
### New files (did not exist in v1.22.0)
|
||||
|
||||
| File | Purpose | What breaks if removed |
|
||||
|------|---------|------------------------|
|
||||
| `pkg/utils/namespace_manager_model.go` | All types: `NamespaceRecord`, `NamespacePhase`, `NamespaceSource`, `NamespaceEvent`, `NamespaceRemovalStrategy`, `ManagedNamespaceWatcherConfig` | Everything — all other files import these types |
|
||||
| `pkg/utils/namespace_manager.go` | `NamespaceManager` interface + `inMemoryNamespaceManager` implementation. `RunManagedNamespaceWatcher()` — the single entry point used by all three components. `NewNamespaceWatcherEventHandlers()` — k8s informer callbacks. `EnsureNamespaceSA` helper call site. | All NSWatcher functionality |
|
||||
| `pkg/utils/namespace_manager_test.go` | Unit + integration tests for NamespaceManager | Tests only |
|
||||
| `pkg/utils/namespace_manager_model_test.go` | Tests for model helpers | Tests only |
|
||||
| `pkg/utils/serviceaccount.go` (was modified, `EnsureNamespaceSA` added at bottom) | `EnsureNamespaceSA(ctx, client, logger, ns)` — creates fission-fetcher SA/Role/RoleBinding in a new namespace idempotently | Pool pods in new namespaces fail to start (no SA to run fetcher) |
|
||||
| `pkg/executor/multitenant/ns_watcher.go` | `StartNSWatcher()` — executor entry point. `registerNamespace()` — calls `AddNamespace` on global resolver + `EnsureNamespaceSA` + all executor types. | Executor never learns about new namespaces |
|
||||
| `pkg/executor/multitenant/namespace_subscriber.go` | `NewNamespaceSubscriber()` — adapter from `NamespaceSubscriber` interface to executor `registerNamespace()` | Same as above |
|
||||
| `pkg/executor/multitenant/ns_watcher_test.go` | Tests | Tests only |
|
||||
| `pkg/executor/multitenant/namespace_subscriber_test.go` | Tests | Tests only |
|
||||
| `pkg/router/ns_watcher.go` | `StartNSWatcher()` — router entry point, 5 lines | Router never learns about new namespaces |
|
||||
| `pkg/router/namespace_subscriber.go` | `NewNamespaceSubscriber()` — adapter calling `HTTPTriggerSet.AddNamespace` | Same as above |
|
||||
| `pkg/router/namespace_subscriber_test.go` | Tests | Tests only |
|
||||
| `pkg/buildermgr/ns_watcher.go` | `StartNSWatcher()` — buildermgr entry point, 5 lines | Buildermgr never learns about new namespaces |
|
||||
| `pkg/buildermgr/namespace_subscriber.go` | `NewNamespaceSubscriber()` — adapter calling `envWatcher.AddNamespace` + `pkgWatcher.AddNamespace` | Same as above |
|
||||
| `pkg/buildermgr/namespace_subscriber_test.go` | Tests | Tests only |
|
||||
| `deploy/multitenant/rbac.yaml` | ClusterRoles + ClusterRoleBindings for all three components (see §6) | Components crash at startup or fail to watch namespaces |
|
||||
|
||||
### Modified files (existed in v1.22.0, we changed them)
|
||||
|
||||
| File | What we added | What breaks if reverted |
|
||||
|------|--------------|-------------------------|
|
||||
| `pkg/utils/namespace.go` | `ManagedNamespaceLabelKey/Value` constants, `ManagedNamespaceLabelSelector()`, `IsManagedNamespace()`, `AddNamespace()` (thread-safe dedup), `Snapshot()` (sorted slice copy under read-lock), `SnapshotWithOptions()` | All callers of `Snapshot()` break — there are many; label constants used by informer filter |
|
||||
| `pkg/utils/namespace_test.go` | Tests for new methods | Tests only |
|
||||
| `pkg/utils/informer.go` | `NewSharedInformerFactoryForNamespaces(namespaces []string)` — creates informer factory filtered to a dynamic list of namespaces | Executor types cannot create per-NS informers for new namespaces |
|
||||
| `pkg/executor/executor.go` | `StartNSWatcher(...)` call added after executor types are initialized | NSWatcher never starts in executor |
|
||||
| `pkg/executor/executortype/executortype.go` | `AddNamespace(ctx, ns, mgr)` added to the `ExecutorType` interface | All three executor types must implement this; compilation fails |
|
||||
| `pkg/executor/executortype/poolmgr/gpm.go` | `AddNamespace()` implementation — creates per-NS informer factory, pod lister, event handlers | poolmgr never picks up functions in new namespaces |
|
||||
| `pkg/executor/executortype/poolmgr/poolpodcontroller.go` | Uses `Snapshot()` in runtime loop instead of static namespace list | Pool pods not created in new namespaces |
|
||||
| `pkg/executor/executortype/newdeploy/newdeploymgr.go` | `AddNamespace()` implementation | newdeploy never picks up functions in new namespaces |
|
||||
| `pkg/executor/executortype/container/containermgr.go` | `AddNamespace()` implementation | container executor never picks up functions in new namespaces |
|
||||
| `pkg/router/router.go` | `StartNSWatcher(...)` call added | NSWatcher never starts in router |
|
||||
| `pkg/router/httpTriggers.go` | `AddNamespace(ns string)` on `HTTPTriggerSet` — starts per-NS informers for HTTPTriggers and Functions | Router ignores HTTPTriggers in new namespaces |
|
||||
| `pkg/router/functionReferenceResolver.go` | Uses `Snapshot()` in runtime loop | Router resolves functions only in statically-configured namespaces |
|
||||
| `pkg/buildermgr/buildermgr.go` | `StartNSWatcher(...)` call added | NSWatcher never starts in buildermgr |
|
||||
| `pkg/buildermgr/envwatcher.go` | `AddNamespace(ns string)` — starts per-NS Environment informer | Buildermgr ignores Environments in new namespaces |
|
||||
| `pkg/buildermgr/pkgwatcher.go` | `AddNamespace(ns string)` — starts per-NS Package informer | Buildermgr ignores Packages in new namespaces |
|
||||
| `pkg/storagesvc/archivePruner.go` | Uses `Snapshot()` instead of static namespace list | Archive pruner only cleans old namespaces |
|
||||
| `.gitignore` | Added `*.token` | Minor — token files would be committed accidentally |
|
||||
| `deploy/multitenant/rbac.yaml` | New file (see above) | — |
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow: from label to HTTP 200
|
||||
|
||||
```
|
||||
kubectl label ns tenant-abc123 fission.io/managed=true
|
||||
│
|
||||
▼
|
||||
k8s API server emits ADDED event on Namespace stream
|
||||
│
|
||||
▼ (within ~50ms)
|
||||
utils.RunManagedNamespaceWatcher ← informer AddFunc
|
||||
│ (all 3 components share this function)
|
||||
▼
|
||||
NamespaceManager.DispatchAdd(ctx, "tenant-abc123")
|
||||
│
|
||||
├──► executor subscriber:
|
||||
│ registerNamespace()
|
||||
│ 1. DefaultNSResolver().AddNamespace("tenant-abc123")
|
||||
│ 2. EnsureNamespaceSA(ctx, client, logger, "tenant-abc123")
|
||||
│ └─ creates fission-fetcher SA + Role + RoleBinding
|
||||
│ 3. poolmgr.AddNamespace("tenant-abc123")
|
||||
│ └─ creates per-NS InformerFactory, pod lister, handlers
|
||||
│ 4. newdeploy.AddNamespace("tenant-abc123")
|
||||
│ 5. container.AddNamespace("tenant-abc123")
|
||||
│
|
||||
├──► router subscriber:
|
||||
│ HTTPTriggerSet.AddNamespace("tenant-abc123")
|
||||
│ └─ starts watching HTTPTriggers + Functions in that NS
|
||||
│
|
||||
└──► buildermgr subscriber:
|
||||
envWatcher.AddNamespace("tenant-abc123")
|
||||
pkgWatcher.AddNamespace("tenant-abc123")
|
||||
└─ starts watching Environments + Packages in that NS
|
||||
|
||||
User creates: fission env create --namespace tenant-abc123 ...
|
||||
fission fn create --namespace tenant-abc123 ...
|
||||
fission httptrigger create --namespace tenant-abc123 ...
|
||||
|
||||
Router picks up HTTPTrigger → resolves Function → cold-starts pod in tenant-abc123
|
||||
│
|
||||
▼
|
||||
HTTP 200 ← function response
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. RBAC Explained
|
||||
|
||||
Three ClusterRoles in `deploy/multitenant/rbac.yaml`:
|
||||
|
||||
### `fission-executor-ns-watcher` (also for router: `fission-router-ns-watcher`)
|
||||
```
|
||||
namespaces: list, watch
|
||||
```
|
||||
Without this: informer fails to start with "Forbidden" — components never learn
|
||||
about new namespaces.
|
||||
|
||||
### `fission-executor-sa-provisioner`
|
||||
```
|
||||
serviceaccounts: get, list, watch, create, update, patch
|
||||
events: create
|
||||
localsubjectaccessreviews: create
|
||||
roles: get, list, watch, create, update, patch
|
||||
rolebindings: get, list, watch, create, update, patch
|
||||
```
|
||||
Why `events.create`: Kubernetes forbids creating a Role that grants permissions
|
||||
the caller does not currently hold. Since `fission-fetcher` gets `events.create`
|
||||
in the Role we create for it, `fission-executor` must hold `events.create` itself
|
||||
to be allowed to create that Role. This is a k8s RBAC escalation prevention rule.
|
||||
|
||||
Why `localsubjectaccessreviews.create`: `EnsureNamespaceSA` calls
|
||||
`setupSAAndRoleBindings` which first checks if the SA already has each permission
|
||||
before creating it — that check uses a `LocalSubjectAccessReview`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Key Design Decisions and Why
|
||||
|
||||
### Decision: pub/sub via `NamespaceManager`, not direct calls
|
||||
**Why not:** simply call `poolmgr.AddNamespace`, `router.AddNamespace` etc.
|
||||
directly from a shared goroutine.
|
||||
**Why pub/sub:** executor, router and buildermgr run as separate processes
|
||||
(different pod). Each process has its own copy of the watcher. Subscribers are
|
||||
registered in-process. This pattern makes each component fully self-contained and
|
||||
testable in isolation. No cross-process coupling.
|
||||
|
||||
### Decision: `fission.io/managed=true` label as the only trigger
|
||||
**Why not:** watch all namespaces, or use a CRD, or use annotations.
|
||||
**Why label:** labels are the idiomatic k8s way to select resources. A label
|
||||
selector in the informer factory (`fission.io/managed=true`) means the informer
|
||||
only receives events for labeled namespaces — zero overhead for the hundreds of
|
||||
system namespaces.
|
||||
|
||||
### Decision: `EnsureNamespaceSA` in executor, not in a separate operator
|
||||
**Why:** the SA must exist before the first pool pod starts. The executor is
|
||||
already in the hot path — it processes the NS event and then immediately triggers
|
||||
pod scheduling. Doing it in a separate controller would introduce a race. Doing it
|
||||
in the executor keeps the lifecycle coupled correctly.
|
||||
|
||||
### Decision: `NamespaceRemovalStrategy = track-only` (not `dispatch-remove`)
|
||||
**Why:** removing a namespace in k8s is already an irreversible event — all
|
||||
resources inside are cascade-deleted by k8s. The executor types detect the pod
|
||||
deletions themselves. Dispatching an explicit "remove" to all subscribers would
|
||||
require each subscriber to implement a cleanup path — added complexity for zero
|
||||
operational benefit in our use case. Can be switched per-component via
|
||||
`ManagedNamespaceWatcherConfig.RemovalStrategy`.
|
||||
|
||||
### Decision: `AddNamespace` is idempotent (safe to call multiple times)
|
||||
**Why:** k8s informers can deliver the same event more than once (resync). Every
|
||||
`AddNamespace` call is a no-op if the namespace is already registered. No locks
|
||||
held across the whole function — `NamespaceResolver.AddNamespace` uses a write
|
||||
lock only for the map write, checks for existence first under the same lock.
|
||||
|
||||
### Decision: global `NamespaceResolver` (`DefaultNSResolver()`) updated once in executor
|
||||
**Why:** `DefaultNSResolver().AddNamespace(ns)` is called exactly once in
|
||||
`registerNamespace()` — before the executor types are called. Each executor type
|
||||
does NOT call it themselves. This avoids a subtle race: if executor type A adds
|
||||
the NS to the global resolver first, and executor type B checks the global resolver
|
||||
as its dedup mechanism, B would see it as already registered and skip — even
|
||||
though B has not actually processed it yet. The correct dedup is per executor type.
|
||||
|
||||
---
|
||||
|
||||
## 8. What Is NOT Done (deliberately deferred)
|
||||
|
||||
| Missing feature | Why deferred | File/interface to extend |
|
||||
|----------------|--------------|--------------------------|
|
||||
| `dispatch-remove` full wiring | Not needed for current use case | `ManagedNamespaceWatcherConfig.RemovalStrategy` — just switch the constant |
|
||||
| Buildermgr ClusterRole in rbac.yaml | Buildermgr uses the same SA as executor in our deployment; check your setup | Add a third ClusterRole/Binding to `deploy/multitenant/rbac.yaml` |
|
||||
| Helm chart integration | We apply rbac.yaml manually. A proper Helm chart would include these RBAC objects | `charts/fission-all/templates/` |
|
||||
| Layer 2 (tenant isolation: per-NS network policy, resource quotas) | Out of scope for Layer 1 | Not started |
|
||||
| Layer 3 (per-tenant auth, billing hooks) | Out of scope | Not started |
|
||||
| `NamespaceManager.DispatchRemove` subscriber wiring | Each subscriber has a `RemoveFunc` stub returning nil | Implement per subscriber |
|
||||
|
||||
---
|
||||
|
||||
## 9. Porting to a New Upstream Version
|
||||
|
||||
When `github.com/fission/fission` releases v1.23 or later, follow this order:
|
||||
|
||||
1. **Check the upstream changelog** for any changes to:
|
||||
- `pkg/utils/namespace.go` — if they renamed or refactored `NamespaceResolver`, our `AddNamespace`/`Snapshot` additions need to be reapplied
|
||||
- `pkg/executor/executortype/executortype.go` — if they changed the `ExecutorType` interface, our `AddNamespace` method needs to be reapplied
|
||||
- `pkg/router/httpTriggers.go` — if `HTTPTriggerSet` changed, our `AddNamespace` method on it needs to be reapplied
|
||||
- `pkg/buildermgr/envwatcher.go`, `pkgwatcher.go` — same
|
||||
- `pkg/utils/informer.go` — if the informer factory pattern changed
|
||||
|
||||
2. **Apply in this order** (each depends on the previous):
|
||||
1. `pkg/utils/namespace_manager_model.go` — pure types, no deps on other changed files
|
||||
2. `pkg/utils/namespace.go` additions (`AddNamespace`, `Snapshot`, label constants)
|
||||
3. `pkg/utils/namespace_manager.go` — depends on model + namespace.go
|
||||
4. `pkg/utils/serviceaccount.go` — add `EnsureNamespaceSA` at the bottom
|
||||
5. `pkg/utils/informer.go` — add `NewSharedInformerFactoryForNamespaces`
|
||||
6. `pkg/executor/executortype/executortype.go` — add `AddNamespace` to interface
|
||||
7. Executor types: `poolmgr/gpm.go`, `newdeploy/newdeploymgr.go`, `container/containermgr.go` — implement `AddNamespace`
|
||||
8. `pkg/executor/multitenant/` — copy the whole package as-is
|
||||
9. `pkg/executor/executor.go` — add `StartNSWatcher` call
|
||||
10. `pkg/router/httpTriggers.go` — add `AddNamespace` method on `HTTPTriggerSet`
|
||||
11. `pkg/router/namespace_subscriber.go`, `pkg/router/ns_watcher.go` — copy as-is
|
||||
12. `pkg/router/router.go` — add `StartNSWatcher` call
|
||||
13. `pkg/buildermgr/envwatcher.go`, `pkgwatcher.go` — add `AddNamespace` method
|
||||
14. `pkg/buildermgr/namespace_subscriber.go`, `pkg/buildermgr/ns_watcher.go` — copy as-is
|
||||
15. `pkg/buildermgr/buildermgr.go` — add `StartNSWatcher` call
|
||||
16. `pkg/storagesvc/archivePruner.go` — replace static namespace list with `Snapshot()`
|
||||
17. `deploy/multitenant/rbac.yaml` — apply unchanged
|
||||
|
||||
3. **Run tests:**
|
||||
```bash
|
||||
go test ./pkg/utils/... ./pkg/executor/... ./pkg/router/... ./pkg/buildermgr/...
|
||||
```
|
||||
|
||||
4. **Build and deploy:**
|
||||
```bash
|
||||
# on VM:
|
||||
docker run --rm -v $PWD:/src -w /src golang:1.26-alpine \
|
||||
sh -c 'go build -o /src/fission-bundle-bin ./cmd/fission-bundle/'
|
||||
docker build -f Dockerfile.mt -t naeel/fission-bundle:vNEW_TAG .
|
||||
docker push naeel/fission-bundle:vNEW_TAG
|
||||
kubectl set image deployment/executor -n fission executor=naeel/fission-bundle:vNEW_TAG
|
||||
kubectl set image deployment/router -n fission router=naeel/fission-bundle:vNEW_TAG
|
||||
kubectl set image deployment/buildermgr -n fission buildermgr=naeel/fission-bundle:vNEW_TAG
|
||||
```
|
||||
|
||||
5. **Run e2e test:**
|
||||
```bash
|
||||
bash ~/terra/fission/scripts/test_layer1.sh
|
||||
# Expected: PASS=5 FAIL=0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Test Coverage
|
||||
|
||||
| Test file | What it covers |
|
||||
|-----------|---------------|
|
||||
| `pkg/utils/namespace_test.go` | `AddNamespace` dedup, `Snapshot` sorted output, `IsManagedNamespace` |
|
||||
| `pkg/utils/namespace_manager_test.go` | `Bootstrap`, `DispatchAdd`/`Remove`/`Resync`, subscriber dispatch, `TestStartManagedNamespaceWatcherIntegration` — full k8s fake informer → subscriber pipeline |
|
||||
| `pkg/utils/namespace_manager_model_test.go` | Model helpers, `Clone`, `IsActive`, `IsTerminal` |
|
||||
| `pkg/executor/multitenant/ns_watcher_test.go` | `registerNamespace` with fake k8s client |
|
||||
| `pkg/executor/multitenant/namespace_subscriber_test.go` | Subscriber adapter |
|
||||
| `pkg/router/namespace_subscriber_test.go` | Router subscriber adapter |
|
||||
| `pkg/buildermgr/namespace_subscriber_test.go` | Buildermgr subscriber adapter |
|
||||
| `~/terra/fission/scripts/test_layer1.sh` | End-to-end: create labeled NS → executor registers it → create env/fn/trigger → call function → HTTP 200 |
|
||||
@@ -0,0 +1,52 @@
|
||||
# Fission Multi-Tenant — Progress
|
||||
|
||||
## Задача
|
||||
Добиться 5/5 PASS в `test_layer1.sh`: динамически добавленный NS с меткой `fission.io/managed=true` должен работать без рестарта Fission.
|
||||
|
||||
---
|
||||
|
||||
## Статус задач
|
||||
|
||||
| # | Задача | Статус |
|
||||
|---|--------|--------|
|
||||
| 1 | Добавить `EnsureNamespaceSA` в `pkg/utils/serviceaccount.go` | ✅ DONE |
|
||||
| 2 | Вызов `EnsureNamespaceSA` из `ns_watcher.go` при регистрации NS | ✅ DONE |
|
||||
| 3 | Сборка образа `naeel/fission-bundle:v1.22.0-multi-ns-8` | ✅ DONE |
|
||||
| 4 | Деплой образа v8 в кластер (executor/router/buildermgr) | ✅ DONE |
|
||||
| 5 | Коммит `161de70` "multi-tenant: EnsureNamespaceSA + ns_watcher SA provisioning (v8)" | ✅ DONE |
|
||||
| 6 | Исправить RBAC: добавить полный набор прав для SA provisioning в `deploy/multitenant/rbac.yaml` | ✅ DONE |
|
||||
| 7 | Применить RBAC через `kubectl apply`, верифицировать SA/Role/RoleBinding | ✅ DONE |
|
||||
| 8 | Коммит RBAC fix | 🔄 IN PROGRESS |
|
||||
| 9 | Запустить `test_layer1.sh`, добиться 5/5 PASS | ⏳ TODO |
|
||||
|
||||
---
|
||||
|
||||
## Текущий результат теста
|
||||
`test_layer1.sh` — 4/5:
|
||||
- Шаг 5 падает: `serviceaccount "fission-fetcher" not found` в NS `l1-test-77773`
|
||||
|
||||
## Диагностика (2026-04-26)
|
||||
- Код `EnsureNamespaceSA` присутствует в `serviceaccount.go` ✅
|
||||
- `ns_watcher.go` строка 168 вызывает `EnsureNamespaceSA` ✅
|
||||
- RBAC: `kubectl auth can-i create serviceaccounts --as=...fission-executor -n l1-test-77773` → **`no`** ❌
|
||||
- ClusterRole `fission-executor-multi-ns` не имеет `create` для `serviceaccounts`, и нет rules для `roles`/`rolebindings`
|
||||
- Вывод: `setupSAAndRoleBindings` вызывается, но получает 403 Forbidden и тихо фейлится → SA не создаётся → pod не стартует
|
||||
|
||||
## Решение
|
||||
Добавить в `deploy/multitenant/rbac.yaml` новый ClusterRole + ClusterRoleBinding с правами:
|
||||
- `serviceaccounts`: `get/list/watch/create/update/patch`
|
||||
- `roles`, `rolebindings`: `get/list/watch/create/update/patch`
|
||||
- `events`: `create`
|
||||
- `localsubjectaccessreviews.authorization.k8s.io`: `create`
|
||||
|
||||
Применить через `kubectl apply`.
|
||||
|
||||
**Пересборка образа НЕ нужна** — логика правильная, проблема только в RBAC.
|
||||
|
||||
## Последняя верификация
|
||||
- `kubectl auth can-i create events --as=system:serviceaccount:fission:fission-executor` → `yes`
|
||||
- `kubectl auth can-i create localsubjectaccessreviews.authorization.k8s.io --as=system:serviceaccount:fission:fission-executor` → `yes`
|
||||
- В новом NS `rbac-verify-83117` автоматически созданы:
|
||||
- `ServiceAccount/fission-fetcher`
|
||||
- `Role/fission-fetcher-role-*`
|
||||
- `RoleBinding/fission-fetcher-rolebinding-*`
|
||||
@@ -0,0 +1,191 @@
|
||||
# Fission — Краткий справочник команд
|
||||
|
||||
> Базовый URL: `https://fission.kube5s.ru/console/api`
|
||||
> Токен передаётся через `X-Auth-Token: <token>` или `Authorization: Bearer <token>`
|
||||
|
||||
```bash
|
||||
BASE="https://fission.kube5s.ru/console/api"
|
||||
T="X-Auth-Token: mylogin@example.com" # demo: любая строка ≥6 символов
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Стандартные операции
|
||||
|
||||
### Функции
|
||||
|
||||
```bash
|
||||
# Создать функцию
|
||||
curl -X POST "$BASE/functions" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"name":"hello","language":"python","code":"def main(event, context): return \"hi\""}'
|
||||
|
||||
# Список функций
|
||||
curl "$BASE/functions" -H "$T"
|
||||
|
||||
# Описание функции
|
||||
curl "$BASE/functions/hello" -H "$T"
|
||||
|
||||
# Вызвать функцию
|
||||
curl -X POST "$BASE/functions/hello/invoke" -H "$T" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
|
||||
# Обновить код
|
||||
curl -X PUT "$BASE/functions/hello/code" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"code":"def main(event, context): return \"v2\""}'
|
||||
|
||||
# Удалить функцию
|
||||
curl -X DELETE "$BASE/functions/hello" -H "$T"
|
||||
```
|
||||
|
||||
**Языки:** `python`, `nodejs`, `go`, `php`, `ruby`
|
||||
|
||||
**Правила имени:** строчные буквы, цифры, дефис; не начинается/не заканчивается дефисом; максимум 57 символов.
|
||||
|
||||
---
|
||||
|
||||
### Environments
|
||||
|
||||
```bash
|
||||
# Список environments
|
||||
curl "$BASE/environments" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Packages
|
||||
|
||||
```bash
|
||||
# Список пакетов
|
||||
curl "$BASE/packages" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### HTTP Triggers
|
||||
|
||||
```bash
|
||||
# Список HTTP triggers
|
||||
curl "$BASE/httptriggers" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Time Triggers (cron)
|
||||
|
||||
```bash
|
||||
# Создать cron
|
||||
curl -X POST "$BASE/timetriggers" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"name":"my-cron","functionName":"hello","cron":"*/5 * * * *"}'
|
||||
|
||||
# Список
|
||||
curl "$BASE/timetriggers" -H "$T"
|
||||
|
||||
# Обновить
|
||||
curl -X PUT "$BASE/timetriggers/my-cron" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"cron":"0 * * * *"}'
|
||||
|
||||
# Удалить
|
||||
curl -X DELETE "$BASE/timetriggers/my-cron" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Прямой вызов по route
|
||||
|
||||
```bash
|
||||
# Вызов без JSON-обёртки (чистый HTTP)
|
||||
curl "https://fission.kube5s.ru/fn/<route>" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Наши расширения
|
||||
|
||||
### Создание из zip-архива
|
||||
|
||||
```bash
|
||||
# Создать функцию из архива напрямую
|
||||
curl -X POST "$BASE/functions" -H "$T" \
|
||||
-F "name=my-fn" -F "language=python" -F "entrypoint=main.handler" \
|
||||
-F "archive=@my-function.zip"
|
||||
|
||||
# Обновить функцию новым архивом
|
||||
curl -X PUT "$BASE/functions/my-fn/archive" -H "$T" \
|
||||
-F "archive=@my-function-v2.zip"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TTL — самоуничтожающиеся функции
|
||||
|
||||
```bash
|
||||
# Функция исчезнет через 1 час
|
||||
curl -X POST "$BASE/functions" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"name":"temp","language":"nodejs","code":"module.exports=async()=>({status:200,body:\"ok\"})","ttl":"1h"}'
|
||||
```
|
||||
|
||||
Форматы TTL: `15m`, `2h`, `1d`, `7d`
|
||||
|
||||
---
|
||||
|
||||
### AI: проверить архив перед деплоем
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/ai/lint-archive" -H "$T" \
|
||||
-F "archive=@my-fn.zip" \
|
||||
-F "language=python" \
|
||||
-F "entrypoint=main.handler"
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"results": [{"file": "main.py", "ok": true}]
|
||||
}
|
||||
```
|
||||
|
||||
Поддерживает: `.py`, `.js`, `.rb`, `.php`
|
||||
|
||||
---
|
||||
|
||||
### Обновить только таймаут
|
||||
|
||||
```bash
|
||||
curl -X PUT "$BASE/functions/hello/timeout" -H "$T" -H "Content-Type: application/json" \
|
||||
-d '{"timeout": 120}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Статус namespace
|
||||
|
||||
```bash
|
||||
# Готовность namespace (stages: создан → RBAC → control-plane)
|
||||
curl "$BASE/ns/status" -H "$T"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Аутентификация / получить namespace
|
||||
|
||||
```bash
|
||||
curl -X POST "$BASE/auth" -H "Content-Type: application/json" \
|
||||
-d '{"token":"mylogin@example.com"}'
|
||||
# → {"ok":true,"namespace":"fission-a3f9c1b2...","email":"..."}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTTP-коды
|
||||
|
||||
| Код | Значение |
|
||||
|-----|---------|
|
||||
| 200 | OK |
|
||||
| 201 | Создано |
|
||||
| 400 | Ошибка валидации |
|
||||
| 401 | Нет/невалидный токен |
|
||||
| 404 | Не найдено (или чужое) |
|
||||
| 409 | Уже существует |
|
||||
| 413 | Слишком большой код/архив |
|
||||
| 502 | Fission внутренняя ошибка |
|
||||
@@ -0,0 +1,248 @@
|
||||
# 2026-04-26 - Layer1 multi-tenant NSWatcher: полный разбор до 5/5 PASS
|
||||
|
||||
## Цель
|
||||
|
||||
Довести `test_layer1.sh` до `PASS=5 FAIL=0` для сценария:
|
||||
|
||||
1. создаётся новый namespace
|
||||
2. namespace получает label `fission.io/managed=true`
|
||||
3. Fission без рестарта подхватывает namespace
|
||||
4. в namespace создаются `Environment`, `Function`, `HTTPTrigger`
|
||||
5. функция успешно вызывается через router
|
||||
|
||||
Ключевое требование: всё должно происходить без rolling restart Fission-компонентов.
|
||||
|
||||
## Исходный симптом
|
||||
|
||||
Первый устойчивый симптом был таким:
|
||||
|
||||
- `test_layer1.sh` стабильно доходил до `4/5`
|
||||
- шаг вызова функции падал
|
||||
- в user namespace наблюдалось:
|
||||
- `FailedCreate`
|
||||
- `serviceaccount "fission-fetcher" not found`
|
||||
|
||||
Это означало, что poolmgr deployment для environment уже создаётся, но pod не может стартовать без `fission-fetcher` ServiceAccount.
|
||||
|
||||
## Что уже было исправлено до RBAC-этапа
|
||||
|
||||
Кодовая часть hot-registration была уже внедрена ранее:
|
||||
|
||||
- `pkg/utils/serviceaccount.go`
|
||||
- добавлена `EnsureNamespaceSA(...)`
|
||||
- `pkg/executor/multitenant/ns_watcher.go`
|
||||
- при регистрации нового namespace вызывается `EnsureNamespaceSA(...)`
|
||||
- образ `naeel/fission-bundle:v1.22.0-multi-ns-8` уже был собран и задеплоен
|
||||
|
||||
То есть логика в коде уже существовала; сбой был не в отсутствии вызова, а в невозможности выполнить его успешно в кластере.
|
||||
|
||||
## Диагностика 1: executor не может создать ServiceAccount/Role/RoleBinding
|
||||
|
||||
Была проведена проверка прав service account `fission-executor`.
|
||||
|
||||
Подтверждено:
|
||||
|
||||
- код `EnsureNamespaceSA` вызывается
|
||||
- `ns_watcher` регистрирует namespace
|
||||
- executor не имеет достаточных RBAC-прав для provisioning ресурсов в новом namespace
|
||||
|
||||
Первый явный пробел:
|
||||
|
||||
- отсутствовали права на:
|
||||
- `serviceaccounts`
|
||||
- `roles`
|
||||
- `rolebindings`
|
||||
|
||||
После начального RBAC fix было видно, что `ServiceAccount/fission-fetcher` уже создаётся, но этого оказалось недостаточно.
|
||||
|
||||
## Диагностика 2: initial RBAC fix оказался неполным
|
||||
|
||||
После расширения прав на `serviceaccounts/roles/rolebindings` тест перестал падать на отсутствии SA, но при детальной диагностике выяснилось, что `EnsureNamespaceSA` всё ещё не может полностью создать `Role` для fetcher.
|
||||
|
||||
Ключевой лог executor:
|
||||
|
||||
```text
|
||||
error while creating role for sa fission-fetcher in namespace diag-ns-82702
|
||||
... is attempting to grant RBAC permissions not currently held:
|
||||
{APIGroups:[""], Resources:["events"], Verbs:["create"]}
|
||||
```
|
||||
|
||||
И дополнительный лог перед этим:
|
||||
|
||||
```text
|
||||
localsubjectaccessreviews.authorization.k8s.io is forbidden
|
||||
```
|
||||
|
||||
### Что это означает
|
||||
|
||||
Функция `setupSAAndRoleBindings()` делает две важные вещи:
|
||||
|
||||
1. пытается проверить уже существующие права через `LocalSubjectAccessReview`
|
||||
2. если прав нет, создаёт `Role` с нужными permission-ами
|
||||
|
||||
Следовательно executor должен иметь не только право создавать `Role/RoleBinding`, но и:
|
||||
|
||||
- `authorization.k8s.io/localsubjectaccessreviews:create`
|
||||
- все permission-ы, которые он пытается делегировать через создаваемую `Role`
|
||||
|
||||
В нашем случае fetcher получает право:
|
||||
|
||||
- `events:create`
|
||||
|
||||
По правилам Kubernetes нельзя создать `Role`, выдающую право, которого нет у самого вызывающего субъекта. Поэтому executor должен был сам иметь `events:create`.
|
||||
|
||||
### Реальный root cause на этом этапе
|
||||
|
||||
`fission-executor` не имел:
|
||||
|
||||
- `events.create`
|
||||
- `localsubjectaccessreviews.create`
|
||||
|
||||
Из-за этого:
|
||||
|
||||
- `ServiceAccount` создавался
|
||||
- но `Role` и `RoleBinding` создавались не полностью или не создавались вовсе
|
||||
- downstream specialization ломалась
|
||||
|
||||
## Исправление 1: полный executor RBAC для dynamic SA provisioning
|
||||
|
||||
В `deploy/multitenant/rbac.yaml` был добавлен и затем расширен `ClusterRole`:
|
||||
|
||||
- `fission-executor-sa-provisioner`
|
||||
|
||||
Итоговый набор прав для него:
|
||||
|
||||
- core:
|
||||
- `serviceaccounts`: `get`, `list`, `watch`, `create`, `update`, `patch`
|
||||
- `events`: `create`
|
||||
- `authorization.k8s.io`:
|
||||
- `localsubjectaccessreviews`: `create`
|
||||
- `rbac.authorization.k8s.io`:
|
||||
- `roles`: `get`, `list`, `watch`, `create`, `update`, `patch`
|
||||
- `rolebindings`: `get`, `list`, `watch`, `create`, `update`, `patch`
|
||||
|
||||
После применения этого манифеста было подтверждено:
|
||||
|
||||
- `kubectl auth can-i create events --as=system:serviceaccount:fission:fission-executor` -> `yes`
|
||||
- `kubectl auth can-i create localsubjectaccessreviews.authorization.k8s.io --as=system:serviceaccount:fission:fission-executor` -> `yes`
|
||||
|
||||
И в новом test namespace автоматически появлялись:
|
||||
|
||||
- `ServiceAccount/fission-fetcher`
|
||||
- `Role/fission-fetcher-role-*`
|
||||
- `RoleBinding/fission-fetcher-rolebinding-*`
|
||||
|
||||
## Изменение симптома после executor-fix
|
||||
|
||||
После полного executor RBAC fix шаг 5 перестал падать с `500` timeout от executor.
|
||||
|
||||
Новый симптом:
|
||||
|
||||
- постоянный `HTTP 404`
|
||||
- router не видел route/function в новом namespace
|
||||
|
||||
Это был важный индикатор того, что executor-path уже работает лучше, а оставшаяся проблема находится в router-path.
|
||||
|
||||
## Диагностика 3: router NSWatcher не мог watch/list namespaces
|
||||
|
||||
Лог router показал прямую ошибку:
|
||||
|
||||
```text
|
||||
failed to list *v1.Namespace: namespaces is forbidden:
|
||||
User "system:serviceaccount:fission:fission-router" cannot list resource
|
||||
"namespaces" at the cluster scope
|
||||
```
|
||||
|
||||
При этом код router уже содержал dynamic namespace watcher:
|
||||
|
||||
- `pkg/router/ns_watcher.go`
|
||||
|
||||
То есть логика была, но RBAC для `fission-router` отсутствовал.
|
||||
|
||||
### Реальный root cause на этом этапе
|
||||
|
||||
`fission-router` не имел cluster-scope прав:
|
||||
|
||||
- `namespaces:list`
|
||||
- `namespaces:watch`
|
||||
|
||||
Из-за этого:
|
||||
|
||||
- router не подхватывал новые labeled namespaces
|
||||
- `HTTPTriggerSet.AddNamespace(...)` не вызывался
|
||||
- HTTP trigger не попадал в router runtime map
|
||||
- вызов функции возвращал `404`
|
||||
|
||||
## Исправление 2: router RBAC для NSWatcher
|
||||
|
||||
В тот же `deploy/multitenant/rbac.yaml` добавлены:
|
||||
|
||||
- `ClusterRole/fission-router-ns-watcher`
|
||||
- `ClusterRoleBinding/fission-router-ns-watcher`
|
||||
|
||||
С правами:
|
||||
|
||||
- core `namespaces`: `list`, `watch`
|
||||
|
||||
После применения подтверждено:
|
||||
|
||||
- `kubectl auth can-i list namespaces --as=system:serviceaccount:fission:fission-router` -> `yes`
|
||||
- `kubectl auth can-i watch namespaces --as=system:serviceaccount:fission:fission-router` -> `yes`
|
||||
|
||||
## Финальная проверка
|
||||
|
||||
После обоих RBAC fixes повторный запуск `test_layer1.sh` дал:
|
||||
|
||||
```text
|
||||
ИТОГ: PASS=5 FAIL=0
|
||||
```
|
||||
|
||||
На шаге 5 функция успешно ответила:
|
||||
|
||||
```text
|
||||
HTTP 200 - hello from layer1
|
||||
```
|
||||
|
||||
## Что именно оказалось правдой по итогу
|
||||
|
||||
Итоговая проблема состояла из двух последовательных RBAC-дырок:
|
||||
|
||||
1. executor не мог полностью provision-ить `fission-fetcher` в динамическом namespace
|
||||
2. router не мог подхватить новый namespace из-за отсутствия namespace watch/list
|
||||
|
||||
То есть код hot-registration в целом был правильный, но runtime contract в Kubernetes RBAC был реализован не полностью.
|
||||
|
||||
## Итоговые изменения
|
||||
|
||||
### Код и манифесты
|
||||
|
||||
- `deploy/multitenant/rbac.yaml`
|
||||
- executor namespace watch
|
||||
- executor SA provisioning RBAC
|
||||
- router namespace watch RBAC
|
||||
|
||||
### Документация
|
||||
|
||||
- `doc/progress.md`
|
||||
- `doc/thinking/2026-04-26-rbac-fix.md`
|
||||
- `doc/thinking/2026-04-26-layer1-pass-detailed.md`
|
||||
|
||||
### Коммиты по ходу исправления
|
||||
|
||||
- `161de70` - `multi-tenant: EnsureNamespaceSA + ns_watcher SA provisioning (v8)`
|
||||
- `8ccc9fb` - первый RBAC commit
|
||||
- `f617913` - полный executor RBAC fix для fetcher role provisioning
|
||||
- `7faaa9d` - router namespace watch RBAC
|
||||
|
||||
## Практический вывод
|
||||
|
||||
Для hot namespace onboarding в Fission недостаточно просто добавить informer-ы в коде.
|
||||
|
||||
Нужно обеспечить весь runtime contract:
|
||||
|
||||
- executor видит namespace
|
||||
- executor может provision-ить service accounts и RBAC в tenant namespace
|
||||
- executor может делегировать все требуемые permission-ы
|
||||
- router видит namespace и подписывается на triggers/functions в нём
|
||||
|
||||
Если хотя бы одно из этих звеньев отсутствует, поведение выглядит как "код вроде есть, но dynamic namespace не работает".
|
||||
@@ -0,0 +1,589 @@
|
||||
# 2026-04-26 — Layer 1 namespace rewrite: подробная логика правок
|
||||
|
||||
## Зачем этот документ
|
||||
|
||||
Нужен не просто список коммитов, а объяснение инженерной логики:
|
||||
|
||||
- что именно было не так в коде;
|
||||
- почему исправление выбрано именно таким;
|
||||
- почему изменения разбиты на маленькие шаги;
|
||||
- какие инварианты я старался сохранить;
|
||||
- что уже исправлено, а что еще нет.
|
||||
|
||||
Этот документ описывает серию маленьких безопасных шагов в ветке
|
||||
`rewrite/layer1-namespace-manager-step1`.
|
||||
|
||||
Основной принцип серии:
|
||||
|
||||
1. Не делать большой взрывной rewrite.
|
||||
2. Сначала сузить race-surface и разъединить старую статическую модель от новой динамической.
|
||||
3. Исправлять реальные дефекты отдельно от mechanical refactor.
|
||||
4. После каждого шага отдельно проверять соответствующий пакет тестами.
|
||||
|
||||
---
|
||||
|
||||
## Исходная архитектурная проблема
|
||||
|
||||
Переделанный Layer 1 жил в гибридном состоянии.
|
||||
|
||||
Старая модель Fission:
|
||||
|
||||
- список resource namespaces задается один раз на старте;
|
||||
- компоненты считают этот список immutable;
|
||||
- informer factories строятся из startup configuration.
|
||||
|
||||
Новая multi-tenant модель:
|
||||
|
||||
- namespace появляется позже, уже после старта процесса;
|
||||
- watcher видит label `fission.io/managed=true`;
|
||||
- компоненты должны подключить новый namespace на лету.
|
||||
|
||||
Из-за этого в коде образовался разрыв между двумя мирами:
|
||||
|
||||
1. Часть кода уже работает как dynamic system.
|
||||
2. Часть кода все еще читает глобальную map namespace-ов напрямую, как будто она immutable.
|
||||
3. В некоторых компонентах startup-path и dynamic-path оказались несимметричными.
|
||||
4. В некоторых местах общий global dedup конфликтует с локальной логикой конкретного компонента.
|
||||
|
||||
Это и есть корневой дефект всей подсистемы: не один конкретный баг, а отсутствие единого namespace lifecycle contract.
|
||||
|
||||
---
|
||||
|
||||
## Что было решено не делать сразу
|
||||
|
||||
Я сознательно не пошел в большой rewrite в один коммит.
|
||||
|
||||
Почему:
|
||||
|
||||
1. Слишком много точек входа: executor, router, buildermgr, storagesvc, utils.
|
||||
2. Если переписать все сразу, невозможно будет локализовать регрессию.
|
||||
3. Уже были реальные functional дефекты в нескольких местах, их удобнее чинить изолированно.
|
||||
4. Пользователь отдельно попросил идти последовательно и проверять после каждого изменения.
|
||||
|
||||
Поэтому выбран bounded rewrite: сначала вычищать старые опасные предположения, затем исправлять функциональные несовпадения, и только потом идти к более крупному NamespaceManager.
|
||||
|
||||
---
|
||||
|
||||
## Инварианты серии
|
||||
|
||||
Во всех шагах я старался держать одинаковые правила.
|
||||
|
||||
### 1. Не ломать действующий onboarding contract
|
||||
|
||||
Если namespace приходит через label watcher, компоненты должны продолжать подключать его без рестарта. Нельзя было ради рефактора возвращаться к статической модели.
|
||||
|
||||
### 2. Не менять лишние контракты одновременно
|
||||
|
||||
Если шаг про snapshot API, он не должен заодно переписывать cleanup semantics.
|
||||
|
||||
### 3. Сначала механические и безопасные сдвиги, потом functional fixes
|
||||
|
||||
Это нужно, чтобы понимать, баг возник из-за новой логики или уже существовал ранее.
|
||||
|
||||
### 4. Каждый шаг должен быть проверяем локально
|
||||
|
||||
После каждого шага запускались тесты по затронутому пакету, а не абстрактное «кажется, всё нормально».
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Snapshot API для namespace resolver
|
||||
|
||||
Коммит: `c987fa0`
|
||||
|
||||
### Что было не так
|
||||
|
||||
`NamespaceResolver` уже имел mutex для записи через `AddNamespace`, но многие потребители читали `FissionResourceNS` напрямую.
|
||||
|
||||
Это означало следующее:
|
||||
|
||||
1. Запись в map уже динамическая.
|
||||
2. Чтение в части мест по-прежнему не thread-safe.
|
||||
3. Код внешне выглядел как безопасный, потому что mutex в структуре есть, но контракт чтения не был централизован.
|
||||
|
||||
То есть защита существовала только наполовину.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `pkg/utils/namespace.go` добавлены:
|
||||
|
||||
- `Snapshot()`
|
||||
- `SnapshotWithOptions()`
|
||||
|
||||
Их логика:
|
||||
|
||||
1. Под read lock взять текущее состояние.
|
||||
2. Скопировать его в detached slice.
|
||||
3. Отсортировать, чтобы получить стабильный детерминированный порядок.
|
||||
|
||||
Почему именно slice snapshot, а не снова map:
|
||||
|
||||
1. Читателям в основном нужен именно проход по namespace-ам.
|
||||
2. Slice удобнее для безопасной итерации.
|
||||
3. Сортировка убирает дрожание порядка и делает поведение более предсказуемым в тестах и логике startup factory generation.
|
||||
|
||||
### Почему это был правильный первый шаг
|
||||
|
||||
Этот шаг почти не меняет бизнес-логику. Он не трогает watchers, RBAC, cleanup, lifecycle events. Он вводит базовый безопасный API, на который потом можно переводить потребителей.
|
||||
|
||||
### Что было переведено сразу
|
||||
|
||||
Чтобы snapshot API не оставался мертвым кодом, на него были переведены:
|
||||
|
||||
- `pkg/utils/informer.go`
|
||||
- startup factory creation в `pkg/executor/executor.go`
|
||||
|
||||
Логика этого выбора:
|
||||
|
||||
1. Это общие helper path.
|
||||
2. Они касаются большого числа компонентов.
|
||||
3. Но при этом change поверхностный: вместо прямой итерации по map берется snapshot.
|
||||
|
||||
### Отдельный мелкий дефект, найденный на шаге 1
|
||||
|
||||
Новые тесты создали локальный `NamespaceResolver` без logger. Выяснилось, что часть методов предполагает ненулевой logger. Это нехорошо само по себе: utility object не должен падать только потому, что его используют вне global singleton.
|
||||
|
||||
Поэтому были добавлены nil checks вокруг debug/info логов в resolver.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
|
||||
Смысл проверки:
|
||||
|
||||
1. Убедиться, что snapshot API корректен как utility layer.
|
||||
2. Убедиться, что startup path executor не поменял поведение.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Исправление namespace routing в serviceaccount checker
|
||||
|
||||
Коммит: `9ce9829`
|
||||
|
||||
### Что было не так
|
||||
|
||||
В `pkg/utils/serviceaccount.go` был более тонкий дефект, чем просто прямое чтение map.
|
||||
|
||||
В `runSACheck()` одна и та же переменная `ns` переиспользовалась внутри цикла по permission groups.
|
||||
|
||||
Смысл проблемы:
|
||||
|
||||
1. Есть исходный base namespace.
|
||||
2. Для fetcher нужен путь через `GetFunctionNS(baseNS)`.
|
||||
3. Для builder нужен путь через `GetBuilderNS(baseNS)`.
|
||||
4. Но код мутировал саму переменную `ns` по мере обхода permission sets.
|
||||
|
||||
Это опасно, потому что builder resolution начинает зависеть от предыдущего шага цикла, а не от исходного namespace.
|
||||
|
||||
Если `FunctionNamespace` и `BuilderNamespace` различаются, route builder SA может поехать.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Изменение было разбито на две части:
|
||||
|
||||
1. Итерироваться не по `FissionResourceNS` напрямую, а по `Snapshot()`.
|
||||
2. Явно вычислять `targetNS` из `baseNS` через отдельный метод `resolveSANamespace(baseNS, saName)`.
|
||||
|
||||
Почему выделен отдельный метод:
|
||||
|
||||
1. Логика namespace routing становится читаемой как отдельный контракт.
|
||||
2. Её можно тестировать отдельно.
|
||||
3. В коде исчезает скрытая мутация переменной цикла.
|
||||
|
||||
### Почему я не переписывал весь serviceaccount.go сразу
|
||||
|
||||
В файле еще остаются спорные места:
|
||||
|
||||
- глобальные `fetcherCheck` / `builderCheck`;
|
||||
- мутация `permission.exists`;
|
||||
- runtime provisioning через `LocalSubjectAccessReview`.
|
||||
|
||||
Но если решать всё сразу, шаг становится слишком широким. На этом этапе была цель исправить именно namespace routing bug и убрать прямую итерацию по общей map.
|
||||
|
||||
### Какой тест был добавлен
|
||||
|
||||
Добавлен unit test на `resolveSANamespace()`:
|
||||
|
||||
- fetcher на default namespace должен идти в function namespace;
|
||||
- builder на default namespace должен идти в builder namespace;
|
||||
- tenant namespace должен сохраняться как tenant namespace.
|
||||
|
||||
Тест важен не из-за синтаксиса, а потому что он фиксирует смысловую развязку между двумя namespace path.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Перевод runtime loops на snapshot API
|
||||
|
||||
Коммит: `6102b27`
|
||||
|
||||
### Что было не так
|
||||
|
||||
Даже после появления snapshot API ещё оставались runtime loops, которые напрямую читали общую map namespace-ов в горячих путях:
|
||||
|
||||
- adopt existing resources;
|
||||
- idle object reaper;
|
||||
- orphan archive pruning.
|
||||
|
||||
Это плохо не только из-за race. Это также концептуально закрепляет старую модель «список namespace-ов — это просто глобальная map, в которую можно смотреть отовсюду».
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Перевёл на `Snapshot()` следующие места:
|
||||
|
||||
- `pkg/executor/executortype/container/containermgr.go`
|
||||
- `pkg/executor/executortype/newdeploy/newdeploymgr.go`
|
||||
- `pkg/executor/executortype/poolmgr/gpm.go`
|
||||
- `pkg/storagesvc/archivePruner.go`
|
||||
|
||||
### Почему именно эти места были хорошим кандидатом
|
||||
|
||||
Потому что это mechanical refactor:
|
||||
|
||||
1. Логика списков не меняется.
|
||||
2. Namespace source меняется с raw map на stable snapshot.
|
||||
3. Поведение должно оставаться тем же, кроме устранения unsafe read.
|
||||
|
||||
### Что это дало
|
||||
|
||||
1. Уменьшило площадь прямого доступа к глобальному mutable состоянию.
|
||||
2. Подготовило код к следующему этапу, когда namespace registry станет ещё более централизованным.
|
||||
3. Сделало background loops более предсказуемыми при одновременном dynamic onboarding.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/executor/... ./pkg/storagesvc/...`
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Исправление buildermgr dedup bug
|
||||
|
||||
Коммит: `56a499a`
|
||||
|
||||
### Это уже не mechanical refactor, а реальный functional fix
|
||||
|
||||
### Что было не так
|
||||
|
||||
`buildermgr.StartNSWatcher()` при появлении нового namespace делал:
|
||||
|
||||
1. `envw.AddNamespace()`
|
||||
2. `pkgw.AddNamespace()`
|
||||
|
||||
Но оба watcher-а использовали один и тот же глобальный dedup через `nsResolver.AddNamespace()`.
|
||||
|
||||
Фактический эффект:
|
||||
|
||||
1. Первый вызов успешно добавляет namespace в global resolver.
|
||||
2. Второй вызов видит, что namespace уже «есть».
|
||||
3. И просто выходит.
|
||||
|
||||
То есть в buildermgr динамический namespace мог получить только часть подписок.
|
||||
|
||||
Это уже не theoretical risk, а реальный дефект логики.
|
||||
|
||||
### Почему проблема архитектурная
|
||||
|
||||
Здесь смешались два уровня ответственности:
|
||||
|
||||
1. Global registry должен знать, что namespace существует.
|
||||
2. Конкретный компонент должен знать, подписался ли он уже на этот namespace.
|
||||
|
||||
Это разные виды dedup.
|
||||
|
||||
Один глобальный dedup не может корректно заменить локальный dedup для двух разных subcomponents.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
Логику развёл по уровням:
|
||||
|
||||
1. В `pkg/buildermgr/ns_watcher.go` global resolver обновляется один раз.
|
||||
2. `environmentWatcher` dedup делает по своей map `envWatchInformer`.
|
||||
3. `packageWatcher` dedup делает по своей map `pkgInformer`.
|
||||
|
||||
### Почему это правильнее
|
||||
|
||||
Теперь структура похожа на executor path:
|
||||
|
||||
1. Глобальный реестр говорит: namespace известен системе.
|
||||
2. Каждый компонент сам решает: свои informers он уже поднял или нет.
|
||||
|
||||
Именно так должен выглядеть multi-component dynamic onboarding.
|
||||
|
||||
### Что я сознательно не делал
|
||||
|
||||
Не добавлял remove/cleanup и не переделывал buildermgr lifecycle целиком. На шаге требовалось только убрать ошибку дедупликации.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/buildermgr/...`
|
||||
|
||||
Тестов в пакете немного, но для этого шага важно было хотя бы подтвердить, что wiring собирается и не поломан compile-time.
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Исправление parity gap в newdeploy
|
||||
|
||||
Коммит: `94f26b6`
|
||||
|
||||
### Что было не так
|
||||
|
||||
`MakeNewDeploy()` на старте процесса регистрировал оба типа handler-ов:
|
||||
|
||||
- `FunctionEventHandlers()`
|
||||
- `EnvEventHandlers()`
|
||||
|
||||
Но `AddNamespace()` для динамически появившегося namespace регистрировал только `FunctionEventHandlers()`.
|
||||
|
||||
Это значит, что два namespace-а с одинаковым содержимым вели себя по-разному только из-за времени появления:
|
||||
|
||||
1. startup namespace обслуживается полным code path;
|
||||
2. dynamic namespace обслуживается урезанным code path.
|
||||
|
||||
Это очень плохое свойство для Layer 1, потому что поведение перестаёт зависеть только от данных и начинает зависеть от истории запуска процесса.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `newdeploy.AddNamespace()` добавил регистрацию `EnvEventHandlers()` рядом с `FunctionEventHandlers()`.
|
||||
|
||||
### Почему fix именно такой
|
||||
|
||||
Потому что это минимальное исправление семантической несимметрии.
|
||||
|
||||
Я не придумывал новую абстракцию, а привёл dynamic path к уже существующему startup contract.
|
||||
|
||||
### Инженерный смысл шага
|
||||
|
||||
Это важный принцип всей серии: если startup-path и late onboarding-path делают похожую работу, они должны проходить через один и тот же контракт, а не через два слегка разных набора side effects.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/executor/executortype/newdeploy`
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Защита router informer maps от гонок
|
||||
|
||||
Коммит: `87477d4`
|
||||
|
||||
### Что было не так
|
||||
|
||||
В router динамический namespace добавляет новые informer-ы в две map:
|
||||
|
||||
- `triggerInformer`
|
||||
- `funcInformer`
|
||||
|
||||
Параллельно `updateRouter()` итерируется по тем же map, собирая триггеры и функции для rebuild router-а.
|
||||
|
||||
Плюс `functionReferenceResolver` получает `funcInformer` и тоже читает его напрямую.
|
||||
|
||||
Это создаёт классическую проблему:
|
||||
|
||||
1. одна goroutine пишет в map;
|
||||
2. другая одновременно по ней итерируется;
|
||||
3. третья читает её через resolver.
|
||||
|
||||
Результат может быть от паники `concurrent map iteration and map write` до тихого чтения неполного состояния.
|
||||
|
||||
### Почему шаг стал чуть шире
|
||||
|
||||
Простой mutex только вокруг `HTTPTriggerSet.AddNamespace()` не решал бы проблему полностью, потому что `functionReferenceResolver` держал свою ссылку на ту же mutable структуру.
|
||||
|
||||
Поэтому понадобилось сделать две вещи одновременно:
|
||||
|
||||
1. Защитить maps в `HTTPTriggerSet` через `RWMutex` и snapshot helpers.
|
||||
2. Дать `functionReferenceResolver` собственный thread-safe путь доступа к informer registry.
|
||||
|
||||
### Что я сделал
|
||||
|
||||
В `HTTPTriggerSet`:
|
||||
|
||||
- добавлен `RWMutex`;
|
||||
- добавлены `snapshotTriggerInformers()`;
|
||||
- добавлены `snapshotFuncInformers()`;
|
||||
- `updateRouter()` и setup handlers теперь работают по snapshot-спискам.
|
||||
|
||||
В `functionReferenceResolver`:
|
||||
|
||||
- добавлен `RWMutex`;
|
||||
- чтение informer-а по namespace теперь под read lock;
|
||||
- добавлен `addInformer()` для безопасного добавления нового namespace.
|
||||
|
||||
В `router.AddNamespace()`:
|
||||
|
||||
- запись в `triggerInformer` и `funcInformer` идёт под lock;
|
||||
- resolver получает новый informer через собственный безопасный метод.
|
||||
|
||||
### Почему именно snapshot-helpers, а не держать lock во время всей итерации
|
||||
|
||||
Потому что rebuild router-а и чтение store-ов могут быть относительно дорогими. Держать глобальный lock на всё это время было бы лишним. Нам нужен был не coarse lock на длинный процесс, а короткий lock на получение стабильного снимка ссылок на informer-ы.
|
||||
|
||||
То есть стратегия такая:
|
||||
|
||||
1. Быстро снять snapshot ссылок.
|
||||
2. Отпустить lock.
|
||||
3. Работать со snapshot уже без блокировки записи.
|
||||
|
||||
Это лучше и по безопасности, и по latency.
|
||||
|
||||
### Проверка шага
|
||||
|
||||
Проверялось:
|
||||
|
||||
- `go test ./pkg/router/...`
|
||||
|
||||
---
|
||||
|
||||
## Почему шаги документировались отдельно
|
||||
|
||||
Я сохранял отдельный thinking-файл на каждый шаг не ради бюрократии, а ради трассируемости.
|
||||
|
||||
Когда изменения маленькие, отдельные документы позволяют понять:
|
||||
|
||||
1. какой дефект исправлял именно этот коммит;
|
||||
2. что было осознанно оставлено за рамками;
|
||||
3. какой тест подтверждал именно этот шаг;
|
||||
4. где functional fix, а где только mechanical safety refactor.
|
||||
|
||||
Именно это позволяет потом анализировать regressions не по памяти, а по истории.
|
||||
|
||||
---
|
||||
|
||||
## Что осталось нерешённым после step 6
|
||||
|
||||
Несмотря на шесть шагов, это ещё не финальный NamespaceManager rewrite.
|
||||
|
||||
Остаются важные вопросы.
|
||||
|
||||
### 1. Нет remove/cleanup semantics
|
||||
|
||||
Система умеет add, но почти не умеет delete/relabel cleanup.
|
||||
|
||||
Что это значит practically:
|
||||
|
||||
- informer-ы и локальные registry entries живут вечно;
|
||||
- once onboarded, always onboarded;
|
||||
- короткоживущие tenant namespace-ы будут оставлять мусор.
|
||||
|
||||
### 2. `serviceaccount.go` всё ещё не идеален
|
||||
|
||||
Текущий `serviceaccount.go` уже лучше, чем до step 2, но файл всё ещё сложный:
|
||||
|
||||
- глобальные `fetcherCheck` / `builderCheck` живут как process-wide mutable objects;
|
||||
- `permission.exists` мутируется в runtime;
|
||||
- provisioning и permission-check тесно сцеплены.
|
||||
|
||||
Это отдельный кандидат на следующий bounded refactor, но уже не маленький mechanical шаг.
|
||||
|
||||
### 3. Глобальный resolver всё ещё остаётся transitional abstraction
|
||||
|
||||
`NamespaceResolver` теперь безопаснее для чтения, но это пока ещё не полноценный NamespaceManager с событиями, remove lifecycle и подписками.
|
||||
|
||||
Он всё ещё ближе к thread-safe registry, чем к полной orchestration layer.
|
||||
|
||||
### 4. Cleanup/restart/backfill lifecycle ещё не централизован
|
||||
|
||||
Часть компонентов уже ближе к единообразию, но по-прежнему нет одного центрального orchestration contract вида:
|
||||
|
||||
- add existing namespaces on startup;
|
||||
- reconcile on relabel;
|
||||
- remove on delete;
|
||||
- rebuild after restart;
|
||||
- re-register late component safely.
|
||||
|
||||
---
|
||||
|
||||
## Почему я не стал сразу делать remove/cleanup
|
||||
|
||||
Потому что это уже следующая категория сложности.
|
||||
|
||||
До step 6 изменения укладывались в схему:
|
||||
|
||||
- локальный и понятный дефект;
|
||||
- ограниченный blast radius;
|
||||
- тестируемый пакет;
|
||||
- отдельный маленький commit.
|
||||
|
||||
Remove/cleanup меняет уже жизненный цикл системы и затрагивает много мест одновременно:
|
||||
|
||||
- watcher behavior;
|
||||
- manager lifecycle;
|
||||
- informer shutdown semantics;
|
||||
- cache invalidation;
|
||||
- resolver state.
|
||||
|
||||
Это не тот шаг, который разумно смешивать с небольшими safety fixes.
|
||||
|
||||
---
|
||||
|
||||
## Почему такая стратегия лучше, чем «переписать всё сразу»
|
||||
|
||||
Потому что сейчас уже есть видимый результат с низким риском:
|
||||
|
||||
1. Уменьшено число прямых доступов к общей mutable map.
|
||||
2. Исправлен реальный functional bug в buildermgr.
|
||||
3. Исправлена реальная логическая ошибка в serviceaccount namespace routing.
|
||||
4. Исправлена несимметрия в newdeploy dynamic path.
|
||||
5. Закрыта явная router race-surface.
|
||||
|
||||
И всё это не одним большим коммитом, а серией шагов с локальной верификацией.
|
||||
|
||||
Для инфраструктурного кода это важнее, чем «красивый большой rewrite», который сложно раскладывать при регрессиях.
|
||||
|
||||
---
|
||||
|
||||
## Какие проверки были прогнаны по ходу серии
|
||||
|
||||
После шагов запускались:
|
||||
|
||||
- `go test ./pkg/utils/...`
|
||||
- `go test ./pkg/executor/...`
|
||||
- `go test ./pkg/storagesvc/...`
|
||||
- `go test ./pkg/buildermgr/...`
|
||||
- `go test ./pkg/router/...`
|
||||
|
||||
Логика была такая:
|
||||
|
||||
1. Не гонять каждый раз всю репу, если шаг локальный.
|
||||
2. Но обязательно проверять затронутый пакет и соседний пакет, если change касается shared utility layer.
|
||||
|
||||
---
|
||||
|
||||
## Текущее состояние после серии
|
||||
|
||||
Серия шагов 1-6 не завершает rewrite, но заметно улучшает базу для следующего этапа.
|
||||
|
||||
Что теперь стало лучше:
|
||||
|
||||
1. Namespace reads стали заметно более дисциплинированными.
|
||||
2. Dynamic namespace onboarding стал логически ровнее между компонентами.
|
||||
3. В router исчезла наиболее явная race-surface на informer maps.
|
||||
4. Buildermgr больше не теряет часть подписок на новый namespace из-за неправильного dedup.
|
||||
|
||||
Что остаётся следующим осмысленным этапом:
|
||||
|
||||
1. Вынесение уже полноценного NamespaceManager как orchestration layer.
|
||||
2. Remove/cleanup lifecycle.
|
||||
3. Разделение discovery, registry и provisioning.
|
||||
4. Дополнительные тесты на restart/relabel/delete/burst onboarding.
|
||||
|
||||
---
|
||||
|
||||
## Отдельная заметка про `serviceaccount.go`
|
||||
|
||||
На момент написания этого документа файл `pkg/utils/serviceaccount.go` был заново перечитан по текущему содержимому. Документ описывает актуальную логику файла в его текущем состоянии, а не только то состояние, которое было в момент коммита step 2.
|
||||
|
||||
Это важно, потому что именно в этом файле пользовательский контекст отдельно предупредил о возможных дополнительных изменениях между сообщениями.
|
||||
@@ -0,0 +1,44 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 1
|
||||
|
||||
## Цель шага
|
||||
|
||||
Начать bounded rewrite Layer 1 без большого взрыва по коду.
|
||||
Первый шаг deliberately узкий:
|
||||
|
||||
- не менять lifecycle namespace onboarding;
|
||||
- не трогать watcher-ы executor/router/buildermgr;
|
||||
- не менять контракты `AddNamespace`;
|
||||
- убрать первые прямые проходы по общей mutable map `FissionResourceNS`.
|
||||
|
||||
## Почему именно так
|
||||
|
||||
Сейчас multi-tenant логика уже динамическая, но многие старые code path все еще читают
|
||||
`DefaultNSResolver().FissionResourceNS` напрямую. Это опасно по двум причинам:
|
||||
|
||||
1. map общая и mutable, а dynamic onboarding меняет ее во время работы процесса;
|
||||
2. часть helper-ов и startup path продолжают жить как будто список namespace-ов immutable.
|
||||
|
||||
Полный rewrite в один шаг дал бы слишком большой blast radius. Поэтому сначала вводится
|
||||
thread-safe snapshot API в namespace layer, а затем существующие потребители переводятся
|
||||
на него по одному.
|
||||
|
||||
## План шага 1
|
||||
|
||||
1. Добавить в `pkg/utils/namespace.go` методы snapshot для plain namespaces и namespaces with options.
|
||||
2. Перевести `pkg/utils/informer.go` на snapshot API.
|
||||
3. Перевести startup factory path в `pkg/executor/executor.go` на snapshot API.
|
||||
4. Добавить unit tests для snapshot behavior.
|
||||
5. Прогнать `go test ./pkg/utils/... ./pkg/executor/...`.
|
||||
|
||||
## Ожидаемый эффект
|
||||
|
||||
- меньше прямых чтений общей map;
|
||||
- появление базового API, через который дальше можно выносить единый NamespaceManager;
|
||||
- нулевое изменение внешнего поведения на этом шаге.
|
||||
|
||||
## Что НЕ делаем на этом шаге
|
||||
|
||||
- не исправляем watcher lifecycle;
|
||||
- не добавляем remove/delete semantics;
|
||||
- не трогаем router race и buildermgr dedup bug;
|
||||
- не меняем RBAC.
|
||||
@@ -0,0 +1,22 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 10
|
||||
|
||||
## Цель шага
|
||||
|
||||
Научить skeleton manager выводить общую phase namespace-а из part states.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем константы состояний частей:
|
||||
- `registering`
|
||||
- `active`
|
||||
- `failed`
|
||||
2. После `MarkPartState()` manager пересчитывает общую phase namespace-а.
|
||||
3. Добавляем unit tests на переходы:
|
||||
- registering -> active
|
||||
- failed -> NamespacePhaseFailed
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не запускаем реальный reconcile loop;
|
||||
- не вызываем subscriber-ов автоматически;
|
||||
- не подключаем manager к runtime.
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 11
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить bootstrap helper для массовой загрузки initial namespace set в manager.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `Bootstrap()` в manager interface и реализацию.
|
||||
2. Метод принимает список namespace-ов и `NamespaceSource`.
|
||||
3. Метод прогоняет namespaces через `Upsert()` как initial discovered set.
|
||||
4. Добавляем unit tests на bootstrap.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем bootstrap к runtime startup path;
|
||||
- не меняем watcher-ы;
|
||||
- не трогаем resolver/SA/runtime.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 12
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить bridge helper между legacy `NamespaceResolver` и новым `NamespaceManager`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем helper `NewBootstrappedNamespaceManager()`.
|
||||
2. Helper берёт snapshot из resolver и bootstraps manager.
|
||||
3. Добавляем unit test на bootstrap from resolver.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем helper к production startup path;
|
||||
- не меняем watcher-ы;
|
||||
- не меняем runtime components.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 13
|
||||
|
||||
## Цель шага
|
||||
|
||||
Централизовать managed namespace label contract в `utils`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем в `utils`:
|
||||
- `ManagedNamespaceLabelKey`
|
||||
- `ManagedNamespaceLabelValue`
|
||||
- `ManagedNamespaceLabelSelector()`
|
||||
- `IsManagedNamespace()`
|
||||
2. Переводим watcher-ы executor/router/buildermgr на единый helper.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем новый manager к watcher-ам;
|
||||
- не меняем поведение onboarding;
|
||||
- не трогаем runtime reconcile.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 14
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить удобные helper-методы для part-state transitions.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В manager interface добавляем:
|
||||
- `MarkPartRegistering()`
|
||||
- `MarkPartActive()`
|
||||
- `MarkPartFailed()`
|
||||
2. Реализуем их поверх `MarkPartState()`.
|
||||
3. Добавляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем helpers к runtime reconcile;
|
||||
- не трогаем watcher-ы и runtime components.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 15
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить utility helper-методы для построения `NamespaceEvent`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NewNamespaceEvent()`.
|
||||
2. Добавляем `ManagedNamespaceEvent()`.
|
||||
3. Добавляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем event helpers к watcher-ам;
|
||||
- не меняем runtime behavior.
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 16
|
||||
|
||||
## Цель шага
|
||||
|
||||
Подготовить lifecycle subscriber contract для будущего reconcile path.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Расширяем `NamespaceSubscriber` методами:
|
||||
- `OnNamespaceAdd()`
|
||||
- `OnNamespaceRemove()`
|
||||
- `OnNamespaceResync()`
|
||||
2. Обновляем тестовую заглушку subscriber-а.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не вызываем subscriber-ов из manager;
|
||||
- не подключаем contract к runtime components.
|
||||
@@ -0,0 +1,22 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 17
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить dispatch helper для прогона namespace через subscriber-ов в add/resync path.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В manager interface добавляем:
|
||||
- `DispatchAdd()`
|
||||
- `DispatchResync()`
|
||||
2. Manager вызывает subscriber-ов последовательно.
|
||||
3. Для каждого subscriber-а manager проставляет part state:
|
||||
- `registering`
|
||||
- `active` или `failed`
|
||||
4. Добавляем unit tests на success и failure path.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем dispatch к production watcher-ам;
|
||||
- не добавляем remove dispatch;
|
||||
- не меняем runtime components.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 18
|
||||
|
||||
## Цель шага
|
||||
|
||||
Подготовить watcher-friendly helper для преобразования Kubernetes Namespace в `NamespaceEvent`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NamespaceEventFromNamespace()`.
|
||||
2. Добавляем unit tests на перенос имени и labels.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем helper к watcher-ам;
|
||||
- не меняем runtime behavior.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 19
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить functional adapter для `NamespaceSubscriber`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NamespaceSubscriberFuncs`.
|
||||
2. Добавляем `Name()/OnNamespaceAdd()/OnNamespaceRemove()/OnNamespaceResync()`.
|
||||
3. Добавляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем adapter к runtime;
|
||||
- не меняем production watcher-ы.
|
||||
@@ -0,0 +1,33 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 2
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать еще один прямой проход по `FissionResourceNS` и закрыть конкретный баг в
|
||||
`pkg/utils/serviceaccount.go`.
|
||||
|
||||
## Проблема
|
||||
|
||||
`runSACheck()` сейчас:
|
||||
|
||||
1. итерируется по `sa.nsResolver.FissionResourceNS` напрямую;
|
||||
2. переиспользует переменную `ns` внутри внутреннего цикла по permissions.
|
||||
|
||||
Из-за этого код выглядит безобидно, но фактически смешивает два разных namespace path:
|
||||
|
||||
- fetcher path через `GetFunctionNS()`;
|
||||
- builder path через `GetBuilderNS()`.
|
||||
|
||||
Если `FunctionNamespace` и `BuilderNamespace` различаются, builder SA может начать
|
||||
резолвиться уже не от исходного namespace, а от результата предыдущего шага цикла.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Берем base namespaces через thread-safe `Snapshot()`.
|
||||
2. Для каждого permission вычисляем `targetNS` из исходного `baseNS`, а не из мутированной переменной.
|
||||
3. Добавляем unit test на routing function/builder namespace.
|
||||
|
||||
## Что НЕ меняем на этом шаге
|
||||
|
||||
- не трогаем глобальные `fetcherCheck` / `builderCheck` структуры;
|
||||
- не меняем `LocalSubjectAccessReview` path;
|
||||
- не делаем большой refactor всего SA provisioning.
|
||||
@@ -0,0 +1,21 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 20
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать первый реальный runtime adapter для `NamespaceManager` в `buildermgr`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем buildermgr namespace subscriber.
|
||||
2. Adapter переиспользует существующие `envWatcher.AddNamespace()` и `packageWatcher.AddNamespace()`.
|
||||
3. `add/resync` path повторяет текущую логику watcher-а:
|
||||
- добавить namespace в resolver;
|
||||
- вызвать env watcher;
|
||||
- вызвать package watcher.
|
||||
4. Добавляем unit test на вызов обоих watcher-ов.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем subscriber к `StartNSWatcher()`;
|
||||
- не меняем remove behavior;
|
||||
- не ломаем текущий production flow.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 21
|
||||
|
||||
## Цель шага
|
||||
|
||||
Свести текущий watcher flow и новый subscriber flow `buildermgr` к одному helper.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `buildermgr/ns_watcher.go` больше не дублирует логику add/resync.
|
||||
2. Watcher вызывает `registerBuilderNamespace()`.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем внешний API watcher-а;
|
||||
- не переключаем `StartNSWatcher()` на `NamespaceManager`.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 22
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить первый runtime adapter для `router` по тому же шаблону, что и для `buildermgr`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем router namespace subscriber.
|
||||
2. Adapter переиспользует существующий `HTTPTriggerSet.AddNamespace()`.
|
||||
3. `add/resync` path прогоняется через общий helper.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем subscriber к `StartNSWatcher()`;
|
||||
- не меняем remove path;
|
||||
- не меняем текущий production flow.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 23
|
||||
|
||||
## Цель шага
|
||||
|
||||
Свести текущий watcher flow и новый subscriber flow `router` к одному helper.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `router/ns_watcher.go` больше не дублирует add/resync логику.
|
||||
2. Watcher вызывает `registerRouterNamespace()`.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не переключаем `StartNSWatcher()` на `NamespaceManager`;
|
||||
- не меняем внешний API watcher-а.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 24
|
||||
|
||||
## Цель шага
|
||||
|
||||
Подготовить `executor/multitenant` к subscriber adapter без смены текущего watcher behavior.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Выделяем отдельный helper для прогона `AddNamespace()` по executor type-ам.
|
||||
2. Оставляем `EnsureNamespaceSA()` в текущем `registerNamespace()`.
|
||||
3. Добавляем unit test на успешный прогон и propagation ошибок.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем `NamespaceManager`;
|
||||
- не меняем внешний API watcher-а.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 25
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить runtime adapter для `executor/multitenant` поверх уже выделенного helper-а.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем executor namespace subscriber.
|
||||
2. `add/resync` path переиспользует `registerNamespace()`.
|
||||
3. Добавляем unit test на вызов executor type-ов.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем subscriber к watcher-у;
|
||||
- не меняем remove path;
|
||||
- не меняем внешний API watcher-а.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 26
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить единый startup bridge для manager: bootstrap model + dispatch в subscriber-ы.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `NamespaceManager` добавляем `BootstrapAndDispatch()`.
|
||||
2. Helper сначала делает `Bootstrap()`, потом вызывает `DispatchAdd()` по каждому namespace.
|
||||
3. Ошибки агрегируются и не останавливают остальные namespace.
|
||||
4. Добавляем unit tests на success и partial-failure.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем helper к production startup path;
|
||||
- не меняем watcher behavior.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 27
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать первый реальный runtime hook на `NamespaceManager` в `buildermgr` watcher.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `buildermgr.StartNSWatcher()` поднимает локальный `NamespaceManager`.
|
||||
2. В manager заранее bootstrapped текущий snapshot resolver-а.
|
||||
3. Watcher `Add/Update` события прогоняет через:
|
||||
- `Upsert()`
|
||||
- `DispatchAdd()` или `DispatchResync()`
|
||||
4. Подписчиком manager-а становится уже существующий `buildermgr` subscriber adapter.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем `registerBuilderNamespace()`;
|
||||
- не добавляем remove path;
|
||||
- не меняем остальные компоненты.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 28
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать такой же runtime hook на `NamespaceManager` в `router` watcher.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `router.StartNSWatcher()` поднимает локальный `NamespaceManager`.
|
||||
2. Manager bootstrapped из текущего resolver snapshot.
|
||||
3. Watcher `Add/Update` события прогоняет через:
|
||||
- `Upsert()`
|
||||
- `DispatchAdd()` или `DispatchResync()`
|
||||
4. Подписчиком manager-а становится router subscriber adapter.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не добавляем remove path;
|
||||
- не меняем `HTTPTriggerSet.AddNamespace()`.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 29
|
||||
|
||||
## Цель шага
|
||||
|
||||
Перевести `executor/multitenant` watcher на тот же manager flow, что уже используется в `buildermgr` и `router`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `StartNSWatcher()` поднимает локальный `NamespaceManager`.
|
||||
2. Manager bootstrapped из resolver snapshot.
|
||||
3. Watcher `Add/Update` события прогоняет через:
|
||||
- `Upsert()`
|
||||
- `DispatchAdd()` или `DispatchResync()`
|
||||
4. Подписчиком manager-а становится executor subscriber adapter.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не добавляем remove path;
|
||||
- не меняем `registerNamespace()` и низкоуровневый executor registration helper.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 3
|
||||
|
||||
## Цель шага
|
||||
|
||||
Срезать еще один слой прямых чтений `DefaultNSResolver().FissionResourceNS` в runtime code path.
|
||||
|
||||
## Почему это отдельный шаг
|
||||
|
||||
После step 1 snapshot API уже существует, но runtime loops в executor и storagesvc все еще
|
||||
читают общую mutable map напрямую. Это не архитектурный rewrite, а чистый safety refactor:
|
||||
|
||||
- `container.AdoptExistingResources()`
|
||||
- `newdeploy.AdoptExistingResources()`
|
||||
- `newdeploy.doIdleObjectReaper()`
|
||||
- `poolmgr.AdoptExistingResources()`
|
||||
- `poolmgr.doIdleObjectReaper()`
|
||||
- `storagesvc.ArchivePruner.getOrphanArchives()`
|
||||
|
||||
## Что меняем
|
||||
|
||||
В этих местах цикл переводится на `DefaultNSResolver().Snapshot()`.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем семантику cleanup;
|
||||
- не меняем behavior watcher-ов;
|
||||
- не добавляем remove semantics;
|
||||
- не исправляем router race и buildermgr dedup на этом шаге.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 30
|
||||
|
||||
## Цель шага
|
||||
|
||||
Закрыть startup gap в `buildermgr`: manager должен отражать и существующие namespace-ы, а не только новые события watcher-а.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `buildermgr.StartNSWatcher()` создаёт пустой `NamespaceManager`.
|
||||
2. После `Subscribe()` выполняется `BootstrapAndDispatch()` по текущему snapshot resolver-а.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем low-level registration helper;
|
||||
- не меняем remove path.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 31
|
||||
|
||||
## Цель шага
|
||||
|
||||
Закрыть startup gap в `router`: локальный manager должен отражать существующие namespace-ы уже на старте.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `router.StartNSWatcher()` создаёт пустой `NamespaceManager`.
|
||||
2. После `Subscribe()` выполняется `BootstrapAndDispatch()` по snapshot resolver-а.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем `HTTPTriggerSet.AddNamespace()`;
|
||||
- не добавляем remove path.
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 32
|
||||
|
||||
## Цель шага
|
||||
|
||||
Закрыть startup gap в `executor/multitenant`: manager должен отражать стартовые namespace-ы и прогонять их через тот же subscriber path.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `StartNSWatcher()` создаёт пустой `NamespaceManager`.
|
||||
2. После `Subscribe()` выполняется `BootstrapAndDispatch()` по snapshot resolver-а.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем `registerNamespace()`;
|
||||
- не добавляем remove path.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 33
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать несоответствие между contract и manager implementation: `OnNamespaceRemove()` уже есть, а `DispatchRemove()` ещё нет.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `NamespaceManager` добавляем `DispatchRemove()`.
|
||||
2. Manager вызывает `OnNamespaceRemove()` у всех subscriber-ов.
|
||||
3. После dispatch namespace переводится в `removed` через `NamespaceEventRemove`.
|
||||
4. Добавляем unit tests на success и failure path.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем remove events в watcher-ы;
|
||||
- не реализуем physical cleanup в runtime components.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 34
|
||||
|
||||
## Цель шага
|
||||
|
||||
Подготовить безопасный helper для delete/tombstone событий Namespace informer-а.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NamespaceFromObject()`.
|
||||
2. Helper поддерживает:
|
||||
- `*corev1.Namespace`
|
||||
- `cache.DeletedFinalStateUnknown`
|
||||
3. Добавляем `NamespaceEventFromObject()`.
|
||||
4. Добавляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не подключаем delete handling в watcher-ы на этом шаге;
|
||||
- не меняем runtime behavior.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 35
|
||||
|
||||
## Цель шага
|
||||
|
||||
Научить watcher-ы фиксировать label-drop/delete в локальном `NamespaceManager`, не трогая реальные runtime регистрации.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Во все три namespace watcher-а добавляем:
|
||||
- `DeleteFunc`
|
||||
- обработку `managed -> unmanaged` в `UpdateFunc`
|
||||
2. При таком событии watcher:
|
||||
- создаёт `NamespaceEventRemove`
|
||||
- записывает его в manager через `Upsert()`
|
||||
- пишет явный log, что runtime cleanup НЕ выполняется
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не вызываем `DispatchRemove()` из watcher-ов;
|
||||
- не удаляем informer-ы, resolver state или runtime registrations.
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 36
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать мёртвый код после перевода watcher-ов на `NamespaceManager` flow.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Удаляем неиспользуемые helper-ы:
|
||||
- `builderNSName()`
|
||||
- `routerNSName()`
|
||||
- `namespaceName()`
|
||||
2. Убираем ставшие неиспользуемыми imports.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime behavior;
|
||||
- не меняем watcher logic.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 37
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать дублирование startup manager flow в трёх namespace watcher-ах.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `utils` добавляем helper `NewWatcherNamespaceManager()`.
|
||||
2. Helper:
|
||||
- создаёт `NamespaceManager`
|
||||
- подписывает subscriber-ов
|
||||
- выполняет `BootstrapAndDispatch()`
|
||||
3. `buildermgr`, `router`, `executor/multitenant` используют новый helper.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем semantics dispatch;
|
||||
- не меняем runtime cleanup policy.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 38
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать повторяющуюся lifecycle логiku namespace watcher-ов.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `utils` добавляем helpers:
|
||||
- `NamespaceBecameUnmanaged()`
|
||||
- `DispatchNamespaceAdd()`
|
||||
- `DispatchNamespaceResync()`
|
||||
- `RecordNamespaceRemoval()`
|
||||
2. `buildermgr`, `router`, `executor/multitenant` используют эти helpers.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime semantics;
|
||||
- remove по-прежнему только bookkeeping, без cleanup.
|
||||
@@ -0,0 +1,43 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 39
|
||||
|
||||
## Цель шага
|
||||
|
||||
Свести три namespace watcher-а к одинаковому lifecycle поведению через общие handlers в `utils`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем helpers:
|
||||
- `HandleWatcherNamespaceAdd()`
|
||||
- `HandleWatcherNamespaceUpdate()`
|
||||
- `HandleWatcherNamespaceDelete()`
|
||||
2. Helpers централизуют:
|
||||
- dispatch add/resync;
|
||||
- remove bookkeeping;
|
||||
- стандартное logging-сообщение.
|
||||
3. `buildermgr`, `router`, `executor/multitenant` переходят на эти helpers.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime cleanup policy;
|
||||
- не меняем manager state model.# 2026-04-26 — NamespaceManager rewrite, step 39
|
||||
|
||||
## Цель шага
|
||||
|
||||
Свести три namespace watcher-а к одинаковому lifecycle поведению через общие handlers в `utils`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем helpers:
|
||||
- `HandleWatcherNamespaceAdd()`
|
||||
- `HandleWatcherNamespaceUpdate()`
|
||||
- `HandleWatcherNamespaceDelete()`
|
||||
2. Helpers централизуют:
|
||||
- dispatch add/resync;
|
||||
- remove bookkeeping;
|
||||
- стандартное logging-сообщение.
|
||||
3. `buildermgr`, `router`, `executor/multitenant` переходят на эти helpers.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime cleanup policy;
|
||||
- не меняем manager state model.
|
||||
@@ -0,0 +1,32 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 4
|
||||
|
||||
## Цель шага
|
||||
|
||||
Исправить реальный functional bug в dynamic onboarding buildermgr.
|
||||
|
||||
## Дефект
|
||||
|
||||
`buildermgr.StartNSWatcher()` вызывает:
|
||||
|
||||
1. `envw.AddNamespace()`
|
||||
2. `pkgw.AddNamespace()`
|
||||
|
||||
Но оба watcher-а используют один и тот же глобальный `nsResolver.AddNamespace()` для dedup.
|
||||
Из-за этого первый вызов добавляет namespace, а второй считает его уже обработанным и
|
||||
выходит раньше времени. В результате у динамического tenant namespace может подняться только
|
||||
Environment informer без Package informer.
|
||||
|
||||
## Исправление
|
||||
|
||||
1. Глобальный resolver обновляется один раз в `buildermgr/ns_watcher.go`.
|
||||
2. `environmentWatcher` dedup делает только по своей map `envWatchInformer`.
|
||||
3. `packageWatcher` dedup делает только по своим map `pkgInformer` / `podInformer`.
|
||||
|
||||
Так buildermgr становится симметричнее executor path: общий registry обновляется один раз,
|
||||
а конкретные компоненты сами решают, подписаны ли они уже на namespace.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не добавляем cleanup/remove semantics;
|
||||
- не меняем router;
|
||||
- не трогаем newdeploy parity gap на этом шаге.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 40
|
||||
|
||||
## Цель шага
|
||||
|
||||
Зафиксировать lifecycle policy для namespace removal в коде явно, а не только комментариями и log-сообщениями.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NamespaceRemovalStrategy`.
|
||||
2. Поддерживаем два режима:
|
||||
- `track-only`
|
||||
- `dispatch-remove`
|
||||
3. Общие watcher handlers принимают strategy.
|
||||
4. Текущий production flow использует `track-only`.
|
||||
5. Добавляем unit tests на оба режима.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не включаем реальный remove dispatch в watcher-ах;
|
||||
- не меняем runtime cleanup policy по умолчанию.
|
||||
@@ -0,0 +1,16 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 41
|
||||
|
||||
## Цель шага
|
||||
|
||||
Довести explicit removal strategy до полного покрытия watcher lifecycle paths.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. `HandleWatcherNamespaceUpdate()` теперь тоже принимает `NamespaceRemovalStrategy`.
|
||||
2. `managed -> unmanaged` path использует ту же policy, что и `DeleteFunc`.
|
||||
3. Добавляем unit test на update-path с `dispatch-remove`.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- текущие watcher-ы остаются на `track-only`;
|
||||
- runtime cleanup policy по умолчанию не меняется.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 42
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать последний крупный слой дублирования в namespace watcher-ах: сами `ResourceEventHandlerFuncs`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `utils` добавляем `NewNamespaceWatcherEventHandlers()`.
|
||||
2. Конструктор собирает общий `Add/Update/Delete` flow на базе уже существующих handler helper-ов.
|
||||
3. `buildermgr`, `router`, `executor/multitenant` используют общий конструктор.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем label selector;
|
||||
- не меняем manager semantics;
|
||||
- не меняем removal policy по умолчанию.
|
||||
@@ -0,0 +1,19 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 43
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать оставшуюся копипасту старта namespace informer-а из `buildermgr`, `router`, `executor/multitenant`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `utils` добавляем `StartManagedNamespaceWatcher()`.
|
||||
2. Helper централизует:
|
||||
- informer factory с label selector;
|
||||
- регистрацию event handlers;
|
||||
- start/cache sync/stop logging через `mgr`.
|
||||
3. Три watcher-а переходят на общий helper.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем lifecycle logic;
|
||||
- не меняем selector contract `fission.io/managed=true`.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 44
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать последний дублирующийся orchestration-код из `StartNSWatcher()` в трёх компонентах.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `utils` добавляем `PrepareManagedNamespaceWatcher()`.
|
||||
2. Helper:
|
||||
- создаёт `NamespaceManager`;
|
||||
- делает bootstrap+dispatch;
|
||||
- собирает общие event handlers.
|
||||
3. `buildermgr`, `router`, `executor/multitenant` используют этот helper.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем subscriber logic;
|
||||
- не меняем managed namespace watcher startup helper;
|
||||
- не меняем removal strategy по умолчанию.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 45
|
||||
|
||||
## Цель шага
|
||||
|
||||
Подготовить компактный status/debug surface для `NamespaceManager`.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `NamespaceManagerSummary`.
|
||||
2. В `NamespaceManager` добавляем `Summary()`.
|
||||
3. Summary считает:
|
||||
- общее число namespace-ов;
|
||||
- число по phase;
|
||||
- список subscriber-ов.
|
||||
4. Добавляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не публикуем summary наружу через HTTP;
|
||||
- не меняем watcher behavior.
|
||||
@@ -0,0 +1,33 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 46
|
||||
|
||||
## Цель шага
|
||||
|
||||
Закрыть маленький пробел в debug surface: `LogNamespaceManagerSummary()` уже используется, но отдельно не тестируется.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем unit test на `LogNamespaceManagerSummary()`.
|
||||
2. Проверяем, что helper безопасен на `nil` logger и не паникует на заполненном summary.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime behavior;
|
||||
- не публикуем summary наружу через HTTP.# 2026-04-26 — NamespaceManager rewrite, step 46
|
||||
|
||||
## Цель шага
|
||||
|
||||
Начать реальное использование `NamespaceManager.Summary()` в orchestration layer.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем helper `LogNamespaceManagerSummary()`.
|
||||
2. `PrepareManagedNamespaceWatcher()` пишет summary после bootstrap.
|
||||
3. В лог попадают:
|
||||
- общее число namespace-ов;
|
||||
- subscriber-ы;
|
||||
- phase counts.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не экспортируем summary наружу через HTTP;
|
||||
- не меняем runtime behavior watcher-ов.
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 47
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать `NamespaceManagerSummary` информативнее для наблюдения за источниками namespace state.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В summary добавляем `SourceCounts`.
|
||||
2. `Summary()` считает namespace-ы по `NamespaceSource`.
|
||||
3. `LogNamespaceManagerSummary()` пишет `source_counts`.
|
||||
4. Обновляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем watcher behavior;
|
||||
- не меняем semantics state transitions.
|
||||
@@ -0,0 +1,33 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 48
|
||||
|
||||
## Цель шага
|
||||
|
||||
Добавить маленький, но полезный helper поверх summary/debug contract: проверку, есть ли вообще живые namespace-ы.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. В `NamespaceManagerSummary` добавляем `HasActiveNamespaces()`.
|
||||
2. Добавляем unit tests на true/false path.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем summary counters;
|
||||
- не меняем watcher behavior.# 2026-04-26 — NamespaceManager rewrite, step 48
|
||||
|
||||
## Цель шага
|
||||
|
||||
Убрать двусмысленность в `NamespaceManagerSummary`: сейчас `TotalNamespaces` включает и removed-записи.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `LiveNamespaces`.
|
||||
2. `Summary()` считает его по `Snapshot()`.
|
||||
3. `LogNamespaceManagerSummary()` пишет оба значения:
|
||||
- `total_namespaces`
|
||||
- `live_namespaces`
|
||||
4. Обновляем unit tests.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем правила хранения removed records;
|
||||
- не меняем watcher behavior.
|
||||
@@ -0,0 +1,35 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 49
|
||||
|
||||
## Цель шага
|
||||
|
||||
Довести `HasActiveNamespaces()` до реального use-site, чтобы helper не оставался чисто декларативным.
|
||||
|
||||
## Что изменено
|
||||
|
||||
1. `LogNamespaceManagerSummary()` теперь пишет флаг `has_active_namespaces`.
|
||||
2. Добавлен unit test на presence и значение этого поля в structured log.
|
||||
|
||||
## Почему это безопасно
|
||||
|
||||
- watcher behavior не меняется;
|
||||
- изменён только debug/logging contract;
|
||||
- покрыто `go test ./pkg/utils/...`.# 2026-04-26 — NamespaceManager rewrite, step 49
|
||||
|
||||
## Цель шага
|
||||
|
||||
Собрать `prepare + start` managed namespace watcher в один общий entrypoint.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `RunManagedNamespaceWatcher()`.
|
||||
2. Helper:
|
||||
- готовит manager;
|
||||
- строит handlers;
|
||||
- запускает managed namespace informer.
|
||||
3. Три `StartNSWatcher()` переходят на новый entrypoint.
|
||||
4. Добавляем минимальный unit test с fake client.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем subscriber logic;
|
||||
- не меняем selector/strategy semantics.
|
||||
@@ -0,0 +1,29 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 5
|
||||
|
||||
## Цель шага
|
||||
|
||||
Исправить несимметрию между startup-path и dynamic namespace onboarding в `newdeploy` executor.
|
||||
|
||||
## Дефект
|
||||
|
||||
На старте `MakeNewDeploy()` регистрирует два вида обработчиков на Fission informers:
|
||||
|
||||
- `FunctionEventHandlers()`
|
||||
- `EnvEventHandlers()`
|
||||
|
||||
Но dynamic `AddNamespace()` регистрировал только `FunctionEventHandlers()`.
|
||||
|
||||
Это означало, что namespace, появившийся после старта процесса, обслуживается не тем же
|
||||
код-path, что namespace, известный на старте. Для multi-tenant Layer 1 это плохая семантика:
|
||||
часть поведения newdeploy зависит не от namespace, а от момента его появления.
|
||||
|
||||
## Исправление
|
||||
|
||||
В `AddNamespace()` добавляется регистрация `EnvEventHandlers()` перед запуском informer factory.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем container executor;
|
||||
- не меняем poolmgr;
|
||||
- не добавляем remove semantics;
|
||||
- не меняем router.
|
||||
@@ -0,0 +1,32 @@
|
||||
# 2026-04-26 — NamespaceManager rewrite, step 50
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать summary/debug surface полезным в реальном watcher lifecycle, а не только на этапе подготовки manager-а.
|
||||
|
||||
## Что изменено
|
||||
|
||||
1. После успешных add/resync/remove transitions watcher helpers теперь пишут компактный summary manager-а.
|
||||
2. Добавлен unit test на add-handler path с проверкой structured-log полей.
|
||||
|
||||
## Что это даёт
|
||||
|
||||
- runtime behavior не меняется;
|
||||
- появляется последовательный debug trail по изменению manager state;
|
||||
- новый helper `HasActiveNamespaces()` теперь используется и в general logging path, и в watcher transition path.# 2026-04-26 — NamespaceManager rewrite, step 50
|
||||
|
||||
## Цель шага
|
||||
|
||||
Сделать orchestration API для managed namespace watcher-а жёстче и читабельнее.
|
||||
|
||||
## Что меняем
|
||||
|
||||
1. Добавляем `ManagedNamespaceWatcherConfig`.
|
||||
2. `PrepareManagedNamespaceWatcher()` и `RunManagedNamespaceWatcher()` принимают config struct.
|
||||
3. Если strategy не задана, используется `track-only`.
|
||||
4. Обновляем unit tests и call sites.
|
||||
|
||||
## Что НЕ меняем
|
||||
|
||||
- не меняем runtime semantics;
|
||||
- не меняем subscriber logic.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user