Аудит провайдера vs каноничный Fission. Добавлено:
Environment:
- builder_image, builder_command для Go и языков с build step
Package:
- deploy_type (literal/source) для переключения deployment/source archive
- loadPackageSourceArchive() — zip-упаковка source_dir
- Убран пустой source:{} из literal mode
Function:
- executor_type (poolmgr/newdeploy/container)
- function_timeout, idle_timeout
- min_scale, max_scale для ExecutionStrategy
Все 21 тест пройден. Обратная совместимость проверена.
Документация: doc/AUDIT_PROVIDER_VS_FISSION_2026-06-03.md
474 lines
18 KiB
Markdown
474 lines
18 KiB
Markdown
# Аудит: Terraform Provider vs Fission Canonical CRD
|
||
|
||
**Дата:** 2026-06-03
|
||
**Ветка:** `feat/provider-audit`
|
||
**Предыдущая версия:** v0.2.4 (ветка `feat/console`)
|
||
|
||
## Методология
|
||
|
||
Сравнение производилось по трём источникам:
|
||
1. **Наш код** — `/terraform/provider/internal/resources/*.go` и `/terraform/provider/internal/client/client.go`
|
||
2. **Fission CRD types.go** — `github.com/fission/fission/pkg/apis/core/v1/types.go` (канонические Go-структуры)
|
||
3. **Реальные CRD объекты в кластере** — `kubectl get` для environments/packages/functions/httptriggers (наши vs CLI-созданные)
|
||
|
||
---
|
||
|
||
## 1. ENVIRONMENT (fission_environment)
|
||
|
||
### 1.1 Что у нас
|
||
|
||
```go
|
||
// environmentResourceModel
|
||
ID, Name, Image, Version(default=3), PoolSize(default=3), Namespace, UID
|
||
```
|
||
|
||
`environmentToUnstructured` генерирует:
|
||
```json
|
||
{
|
||
"spec": {
|
||
"version": 3,
|
||
"runtime": { "image": "..." },
|
||
"poolsize": 3
|
||
}
|
||
}
|
||
```
|
||
|
||
### 1.2 Что делает Fission CLI (`fission env create`)
|
||
|
||
Fission CLI (из `environment/create.go`) создает полный `EnvironmentSpec`:
|
||
```json
|
||
{
|
||
"spec": {
|
||
"version": 3,
|
||
"runtime": {
|
||
"image": "ghcr.io/fission/python-env",
|
||
"container": { "name": "env-name", "resources": {} },
|
||
"podspec": { "containers": [{"name": "env-name", "resources": {}}] }
|
||
},
|
||
"builder": {
|
||
"image": "ghcr.io/fission/go-builder",
|
||
"command": "build",
|
||
"container": { "name": "builder", "resources": {} },
|
||
"podspec": { "containers": [{"name": "builder", "resources": {}}] }
|
||
},
|
||
"poolsize": 3,
|
||
"resources": {},
|
||
"imagepullsecret": "",
|
||
"keeparchive": false
|
||
}
|
||
}
|
||
```
|
||
|
||
### 1.3 Реальное сравнение в кластере
|
||
|
||
| Поле | Наш (tf-python-env) | CLI (python) | Вердикт |
|
||
|------|---------------------|--------------|---------|
|
||
| `spec.version` | 3 | 3 | ✅ OK |
|
||
| `spec.runtime.image` | ✅ | ✅ | ✅ OK |
|
||
| `spec.runtime.container` | ❌ отсутствует | `{name, resources}` | ⚠️ Fission заполняет defaults — не критично |
|
||
| `spec.runtime.podspec` | ❌ отсутствует | `{containers}` | ⚠️ Fission заполняет defaults — не критично |
|
||
| `spec.builder` | ❌ ОТСУТСТВУЕТ | `{image, command, container, podspec}` | 🔴 **КРИТИЧНО для Go** |
|
||
| `spec.poolsize` | 3 | 3 | ✅ OK |
|
||
| `spec.resources` | ❌ отсутствует | `{}` | ⚠️ Defaults — не критично |
|
||
| `spec.imagepullsecret` | ❌ | `""` | ⚠️ Можно добавить позже |
|
||
| `spec.keeparchive` | ❌ | `false` | ⚠️ Нужно для JVM — не критично сейчас |
|
||
|
||
### 1.4 Выводы по Environment
|
||
|
||
**Критичный баг:** Невозможно создать environment с builder (нет полей `builder_image`, `builder_command`). Это блокирует Go, любой язык с build step.
|
||
|
||
**Что добавить (приоритетно):**
|
||
- `builder_image` (string, optional) → `spec.builder.image`
|
||
- `builder_command` (string, optional) → `spec.builder.command`
|
||
|
||
**Что можно добавить позже:**
|
||
- `resources` (object) → `spec.resources`
|
||
- `imagepullsecret` (string) → `spec.imagepullsecret`
|
||
- `keeparchive` (bool) → `spec.keeparchive`
|
||
- `runtime_container_name` — Fission автозаполняет, мы не ставим, k8s принимает без него
|
||
|
||
**Что НЕ нужно (Fission автозаполняет):**
|
||
- `spec.runtime.container`, `spec.runtime.podspec` — заливаются defaults на стороне сервера
|
||
- `spec.builder.container`, `spec.builder.podspec` — аналогично
|
||
|
||
---
|
||
|
||
## 2. PACKAGE (fission_package)
|
||
|
||
### 2.1 Что у нас
|
||
|
||
```go
|
||
// packageResourceModel
|
||
ID, Name, Environment, SourceDir, CodePath, CodeHash, BuildCmd, Namespace, UID, BuildStatus, BuildLog
|
||
```
|
||
|
||
`packageToUnstructured` генерирует:
|
||
```json
|
||
{
|
||
"spec": {
|
||
"deployment": {
|
||
"type": "literal",
|
||
"literal": "base64..."
|
||
},
|
||
"environment": { "name": "...", "namespace": "..." },
|
||
"source": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.2 Что делает Fission CLI
|
||
|
||
Для **deploy-only** (literal):
|
||
```json
|
||
{
|
||
"spec": {
|
||
"deployment": {
|
||
"type": "literal",
|
||
"literal": "base64...",
|
||
"checksum": {}
|
||
},
|
||
"environment": { "name": "...", "namespace": "..." },
|
||
"source": { "checksum": {} }
|
||
},
|
||
"status": {
|
||
"buildstatus": "none"
|
||
}
|
||
}
|
||
```
|
||
|
||
Для **source-with-builder** (Go, Node с build):
|
||
```json
|
||
{
|
||
"spec": {
|
||
"source": {
|
||
"type": "literal",
|
||
"literal": "base64-of-zip...",
|
||
"checksum": {}
|
||
},
|
||
"environment": { "name": "...", "namespace": "..." },
|
||
"buildcmd": "build"
|
||
},
|
||
"status": {
|
||
"buildstatus": "pending"
|
||
}
|
||
}
|
||
```
|
||
|
||
Для **large archives** (>256KB):
|
||
- Загрузка через StorageSvc `/v1/archive` (multipart POST)
|
||
- В CRD сохраняется `type: "url"`, `url: "http://storagesvc/v1/archive?id=..."`
|
||
|
||
### 2.3 Реальное сравнение в кластере
|
||
|
||
| Поле | Наш (tf-hello-pkg) | CLI (hello-*) | Вердикт |
|
||
|------|---------------------|---------------|---------|
|
||
| `spec.deployment.type` | `"literal"` | `"literal"` | ✅ OK |
|
||
| `spec.deployment.literal` | ✅ base64 | ✅ base64 | ✅ OK |
|
||
| `spec.deployment.checksum` | ❌ отсутствует | `{}` | ⚠️ K8s принимает без, но лучше добавить |
|
||
| `spec.environment` | ✅ | ✅ | ✅ OK |
|
||
| `spec.source` | `{}` (пустая map) | `{"checksum":{}}` | 🟡 **БАГ**: мы ставим пустой source — не мешает, но мусор |
|
||
| `spec.buildcmd` | ✅ (если задан) | ✅ | ✅ OK |
|
||
| `status.buildstatus` | `"none"` (от k8s default) | `"none"` | ✅ OK (k8s сам ставит) |
|
||
|
||
### 2.4 Что ОТСУТСТВУЕТ для builder pipeline (Go)
|
||
|
||
Для Go-функций нужен **source** package (не deployment):
|
||
1. Код упаковывается в zip
|
||
2. zip кодируется в base64 → `spec.source.literal` (если <256KB)
|
||
3. `spec.source.type` = `"literal"`
|
||
4. `spec.deployment` = пусто
|
||
5. `spec.buildcmd` = `"build"` (или пользовательская)
|
||
6. `status.buildstatus` = `"pending"` → builder собирает → `"succeeded"`/`"failed"`
|
||
7. После build: `spec.deployment` заполняется builder'ом (url на StorageSvc)
|
||
|
||
### 2.5 Выводы по Package
|
||
|
||
**Баг (некритичный):** Мы ВСЕГДА ставим `"source": {}` — пустой объект. Fission ставит `"source": {"checksum": {}}`. Оба варианта работают, но чистый вариант — не ставить source вообще если нет source archive.
|
||
|
||
**Что добавить (приоритетно):**
|
||
- **Режим source archive** — для Go и языков с build step. Нужно:
|
||
- Флаг/переключатель: deployment-only vs source-with-build
|
||
- Упаковка source_dir в zip → base64 → `spec.source.literal`
|
||
- Проверка размера <256KB (лимит ArchiveLiteralSizeLimit)
|
||
- Очистка `spec.deployment` при source mode
|
||
- `status.buildstatus` = `"pending"` на create
|
||
|
||
**Что можно добавить позже:**
|
||
- StorageSvc загрузка для >256KB архивов
|
||
- `spec.source.checksum`
|
||
- Поддержка `type: "url"` (для уже загруженных архивов)
|
||
|
||
---
|
||
|
||
## 3. FUNCTION (fission_function)
|
||
|
||
### 3.1 Что у нас
|
||
|
||
```go
|
||
// functionResourceModel
|
||
ID, Name, Environment, PackageName, Entrypoint, Namespace, UID
|
||
```
|
||
|
||
`functionToUnstructured` генерирует:
|
||
```json
|
||
{
|
||
"spec": {
|
||
"environment": { "name": "...", "namespace": "..." },
|
||
"InvokeStrategy": {
|
||
"ExecutionStrategy": { "ExecutorType": "poolmgr" },
|
||
"StrategyType": "execution"
|
||
},
|
||
"package": {
|
||
"packageref": { "name": "...", "namespace": "..." },
|
||
"functionName": "main.main"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 Что делает Fission CLI (`fission fn create`)
|
||
|
||
```json
|
||
{
|
||
"spec": {
|
||
"environment": { "name": "...", "namespace": "..." },
|
||
"InvokeStrategy": {
|
||
"ExecutionStrategy": {
|
||
"ExecutorType": "poolmgr",
|
||
"MaxScale": 0,
|
||
"MinScale": 0,
|
||
"SpecializationTimeout": 120,
|
||
"TargetCPUPercent": 0
|
||
},
|
||
"StrategyType": "execution"
|
||
},
|
||
"package": {
|
||
"packageref": {
|
||
"name": "...",
|
||
"namespace": "...",
|
||
"resourceversion": "6000598"
|
||
},
|
||
"functionName": ""
|
||
},
|
||
"functionTimeout": 60,
|
||
"idletimeout": 120,
|
||
"concurrency": 500,
|
||
"requestsPerPod": 1,
|
||
"resources": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 Реальное сравнение в кластере
|
||
|
||
| Поле | Наш (tf-hello-fn) | CLI (fn-js-acc) | Вердикт |
|
||
|------|---------------------|-----------------|---------|
|
||
| `spec.environment` | ✅ | ✅ | ✅ OK |
|
||
| `spec.InvokeStrategy.ExecutionStrategy.ExecutorType` | `"poolmgr"` | `"poolmgr"` | ✅ OK |
|
||
| `spec.InvokeStrategy.ExecutionStrategy.MaxScale` | ❌ отсутствует | `0` | ⚠️ Defaults работают, но лучше ставить |
|
||
| `spec.InvokeStrategy.ExecutionStrategy.MinScale` | ❌ | `0` | ⚠️ |
|
||
| `spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout` | ❌ | `120` | ⚠️ |
|
||
| `spec.InvokeStrategy.ExecutionStrategy.TargetCPUPercent` | ❌ | `0` | ⚠️ Не критично |
|
||
| `spec.InvokeStrategy.StrategyType` | `"execution"` | `"execution"` | ✅ OK |
|
||
| `spec.package.packageref.resourceversion` | ❌ отсутствует | ✅ | 🟡 CLI ставит для оптимизации, мы — нет |
|
||
| `spec.package.functionName` | ✅ `"main.main"` | `""` (или функция) | ✅ OK |
|
||
| `spec.functionTimeout` | ❌ | `60` | 🟡 Полезно для управления таймаутами |
|
||
| `spec.idletimeout` | ❌ | `120` | 🟡 Полезно для scale-to-zero |
|
||
| `spec.concurrency` | ❌ | `500` | ⚠️ |
|
||
| `spec.requestsPerPod` | ❌ | `1` | ⚠️ |
|
||
| `spec.resources` | ❌ | `{}` | ⚠️ |
|
||
|
||
### 3.4 Выводы по Function
|
||
|
||
**Критичных багов нет.** Наши функции работают, потому что k8s/Fission подставляет defaults. НО:
|
||
|
||
**Что добавить (приоритетно):**
|
||
- `executor_type` (string, optional, default="poolmgr") → для newdeploy/container strategies
|
||
- `function_timeout` (int, optional) → `spec.functionTimeout` — важно для долгих функций
|
||
- `idle_timeout` (int, optional) → `spec.idletimeout` — управление scale-to-zero
|
||
- `min_scale` / `max_scale` (int, optional) → ExecutionStrategy — для newdeploy
|
||
|
||
**Что можно добавить позже:**
|
||
- `concurrency` (int) → `spec.concurrency`
|
||
- `requests_per_pod` (int) → `spec.requestsPerPod`
|
||
- `specialization_timeout` (int) → `spec.InvokeStrategy.ExecutionStrategy.SpecializationTimeout`
|
||
- `resources` (object) → CPU/MEM limits
|
||
- `secrets`, `configmaps` (list) → volume mounts
|
||
|
||
---
|
||
|
||
## 4. HTTP TRIGGER (fission_http_trigger)
|
||
|
||
### 4.1 Что у нас
|
||
|
||
```go
|
||
// httpTriggerResourceModel
|
||
ID, Name, Function, URL, Methods, CreateIngress, Host, Namespace, UID
|
||
```
|
||
|
||
`httpTriggerToUnstructured` генерирует:
|
||
```json
|
||
{
|
||
"spec": {
|
||
"relativeurl": "/tf-hello",
|
||
"methods": ["GET"],
|
||
"functionref": { "type": "name", "name": "tf-hello-fn" },
|
||
"createingress": false
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 Что делает Fission CLI
|
||
|
||
```json
|
||
{
|
||
"spec": {
|
||
"relativeurl": "/hello",
|
||
"methods": ["GET"],
|
||
"functionref": {
|
||
"type": "name",
|
||
"name": "hello",
|
||
"functionweights": null
|
||
},
|
||
"createingress": false,
|
||
"host": "",
|
||
"ingressconfig": {
|
||
"annotations": null,
|
||
"host": "*",
|
||
"path": "/hello",
|
||
"tls": ""
|
||
},
|
||
"method": "",
|
||
"prefix": ""
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.3 Реальное сравнение в кластере
|
||
|
||
| Поле | Наш (tf-hello-route) | CLI (hello-route) | Вердикт |
|
||
|------|----------------------|-------------------|---------|
|
||
| `spec.relativeurl` | ✅ | ✅ | ✅ OK |
|
||
| `spec.methods` | ✅ | ✅ | ✅ OK |
|
||
| `spec.functionref.type` | `"name"` | `"name"` | ✅ OK |
|
||
| `spec.functionref.name` | ✅ | ✅ | ✅ OK |
|
||
| `spec.functionref.functionweights` | ❌ | `null` | ✅ Не нужно |
|
||
| `spec.createingress` | ✅ | ✅ | ✅ OK |
|
||
| `spec.host` | ❌ (если пусто) | `""` | ✅ Не критично |
|
||
| `spec.ingressconfig` | Частично (host) | Полный | ⚠️ IngressConfig неполный |
|
||
| `spec.method` | ❌ | `""` | ✅ Legacy, не нужно |
|
||
| `spec.prefix` | ❌ | `""` | ⚠️ Для prefix routing — добавить |
|
||
|
||
### 4.4 Выводы по HTTPTrigger
|
||
|
||
**Багов нет.** Работает корректно. Мелкие расхождения не влияют.
|
||
|
||
**Что можно добавить позже:**
|
||
- `prefix` (string) → `spec.prefix` — для prefix-based routing
|
||
- `keep_prefix` (bool) → `spec.keepPrefix`
|
||
- Полный `ingressconfig` (annotations, path, tls) — при `create_ingress=true`
|
||
- `function_weights` (map) → для canary deployments
|
||
|
||
---
|
||
|
||
## 5. CLIENT (client.go)
|
||
|
||
### 5.1 Оценка
|
||
|
||
**Код корректный.** Чистый CRUD через `dynamic.Interface`:
|
||
- 4 GVR определения (environments, packages, functions, httptriggers)
|
||
- CRUD для каждого: Create/Get/Update/Delete
|
||
- `IsNotFound()` для обработки 404
|
||
- `New()` строит config из kubeconfig + context
|
||
|
||
**Расхождений с Fission нет** — это наш собственный low-level клиент для работы с CRD.
|
||
|
||
---
|
||
|
||
## 6. VALIDATION (validation_helpers.go, entrypoint validation)
|
||
|
||
### 6.1 Оценка
|
||
|
||
- `ensureEnvironmentExists` — ✅ корректно (проверяет наличие env перед созданием pkg/fn)
|
||
- `ensurePackageExists` — ✅ корректно
|
||
- `validateEntrypointAgainstPackageSource` — ⚠️ Проверяет только Python `def funcname(`. Не проверяет:
|
||
- Node: `module.exports` или `export function`
|
||
- Go: plugin symbol
|
||
- PHP: `function handler(`
|
||
- Ruby: `def handler`
|
||
|
||
**Это допустимо** — избыточная валидация может мешать. Лучше валидировать только точно известные паттерны.
|
||
|
||
---
|
||
|
||
## 7. СВОДНАЯ ТАБЛИЦА ПРИОРИТЕТОВ
|
||
|
||
### 🔴 Критично (блокирует функционал)
|
||
|
||
| # | Ресурс | Проблема | Решение |
|
||
|---|--------|----------|---------|
|
||
| 1 | Environment | Нет builder support | Добавить `builder_image`, `builder_command` |
|
||
| 2 | Package | Нет source archive mode | Добавить zip-упаковку source_dir → `spec.source.literal` |
|
||
|
||
### 🟡 Важно (улучшает пользовательский опыт)
|
||
|
||
| # | Ресурс | Проблема | Решение |
|
||
|---|--------|----------|---------|
|
||
| 3 | Function | Захардкожен poolmgr | Добавить `executor_type` с optional default |
|
||
| 4 | Function | Нет пользовательских таймаутов | Добавить `function_timeout`, `idle_timeout` |
|
||
| 5 | Function | Нет min/max scale | Добавить `min_scale`, `max_scale` |
|
||
| 6 | Package | Пустой `source: {}` мусор | Убрать пустой source из payload |
|
||
|
||
### ⚪ Не критично (можно позже)
|
||
|
||
| # | Ресурс | Проблема |
|
||
|---|--------|----------|
|
||
| 7 | Environment | Нет resources, imagepullsecret, keeparchive |
|
||
| 8 | Function | Нет concurrency, requestsPerPod, resources, secrets, configmaps |
|
||
| 9 | HTTPTrigger | Нет prefix, keepPrefix, полного ingressconfig |
|
||
| 10 | Package | Нет StorageSvc загрузки (>256KB) |
|
||
| 11 | Package | Нет checksum |
|
||
|
||
---
|
||
|
||
## 8. ПЛАН РЕАЛИЗАЦИИ (предлагаемый)
|
||
|
||
### Этап 1: Builder support (Environment + Package)
|
||
|
||
**environment_resource.go:**
|
||
- Добавить поля `builder_image` и `builder_command` в модель и schema
|
||
- Добавить `spec.builder` в `environmentToUnstructured` (если builder_image задан)
|
||
- Обновить `unstructuredToEnvironmentModel` для чтения builder полей
|
||
|
||
**package_resource.go:**
|
||
- Добавить поле `deploy_type` (string: `"literal"` или `"source"`, default `"literal"`)
|
||
- При `deploy_type = "source"`: zip source_dir → base64 → `spec.source.literal`, `spec.deployment` пустой
|
||
- Убрать пустой `"source": {}` при deploy_type = "literal"
|
||
- Добавить base64 size check (<256KB) при literal mode
|
||
|
||
### Этап 2: Function tuning
|
||
|
||
**function_resource.go:**
|
||
- Добавить optional поля: `executor_type`, `function_timeout`, `idle_timeout`, `min_scale`, `max_scale`
|
||
- Обновить `functionToUnstructured` для заполнения ExecutionStrategy полностью
|
||
- Обновить `unstructuredToFunctionModel` для чтения новых полей
|
||
|
||
### Этап 3: Мелкие улучшения
|
||
- HTTPTrigger: prefix, keepPrefix
|
||
- Package: checksum
|
||
- Environment: resources, imagepullsecret
|
||
|
||
---
|
||
|
||
## 9. ВЫВОД
|
||
|
||
Наш провайдер **работает корректно для основного сценария**: Python/Node/PHP/Ruby/Perl literal deployment + poolmgr executor. Все критические поля (version, runtime.image, poolsize, deployment.literal, functionName, relativeurl, methods) генерируются правильно.
|
||
|
||
**Главные пробелы:**
|
||
1. Нет builder support → Go и любые compiled languages не работают через builder pipeline
|
||
2. Нет source archive → только deployment-only (literal из одного файла)
|
||
3. Function executor hardcoded to poolmgr → нет newdeploy/container strategy
|
||
4. Нет пользовательских таймаутов
|
||
|
||
Ни один из пробелов не является **ошибкой** в существующем коде — это **недостающий функционал**. То, что есть, соответствует канону Fission.
|