From 55758b62802a9dfa429cd1f6537efa6a32daf697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 25 May 2026 10:04:23 +0400 Subject: [PATCH] admin-console: add DeepSeek handoff instructions --- admin-console/DEEPSEEK_HANDOFF.md | 164 ++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 admin-console/DEEPSEEK_HANDOFF.md diff --git a/admin-console/DEEPSEEK_HANDOFF.md b/admin-console/DEEPSEEK_HANDOFF.md new file mode 100644 index 0000000..a69f0cc --- /dev/null +++ b/admin-console/DEEPSEEK_HANDOFF.md @@ -0,0 +1,164 @@ +# Handoff: admin-console — задание для DeepSeek + +## Контекст + +Репозиторий `fission-console`, ветка `restructure/repo-layout`. + +Это **Fission Serverless Platform** — кастомная установка. Клиентская консоль (`client-console/`) уже готова и работает в проде. Нужно реализовать **admin-console** (`admin-console/`) — панель для оператора платформы (просмотр всех клиентов, использование ресурсов, данные для бухгалтерии). + +--- + +## Что уже сделано (скелет) + +``` +admin-console/ + cmd/server/main.go — точка входа, порт 8091, требует ADMIN_TOKEN + internal/api/server.go — роуты: /admin/api/namespaces, /usage, /users + internal/metrics/k8s.go — k8s-клиент (in-cluster), заглушки для реальных данных + internal/model/types.go — типы NamespaceInfo, UsageEntry, UserInfo + deploy/admin.yaml — K8s Deployment + ClusterRole (read-only всё) + ui/index.html — базовый UI (тёмная тема, 3 вкладки) + go.mod — module admin-console, зависит от k8s.io/client-go v0.31.0 +``` + +--- + +## Что нужно реализовать + +### 1. `internal/metrics/k8s.go` — подсчёт Fission CRD + +Метод `ListClientNamespaces` должен для каждого namespace считать объекты через Dynamic client: + +```go +import ( + "k8s.io/client-go/dynamic" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +dynamicClient, _ := dynamic.NewForConfig(cfg) + +gvr := schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "functions"} +list, err := dynamicClient.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{}) +info.Functions = len(list.Items) +``` + +Аналогично для: `packages`, `environments`, `httptriggers`, `timetriggers`. + +Сохрани `dynamicClient` в структуру `K8sClient` и используй его в обоих методах. + +### 2. `internal/metrics/k8s.go` — S3-хранилище в `CollectUsage` + +Поле `StorageGB` — читать из S3. Конфигурация через env: +- `S3_ENDPOINT` = `https://s3.msk-1.ngcloud.ru` +- `S3_BUCKET` = `sless-functions` +- `S3_ACCESS_KEY`, `S3_SECRET_KEY` + +Использовать `github.com/aws/aws-sdk-go-v2`. Для каждого namespace prefix = `{namespace}/`, суммировать размер всех объектов. + +```go +import ( + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/s3" + awscfg "github.com/aws/aws-sdk-go-v2/config" +) + +cfg, _ := awscfg.LoadDefaultConfig(ctx, + awscfg.WithRegion("ru-msk-1"), + awscfg.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), + awscfg.WithEndpointResolverWithOptions(...), // custom endpoint +) +client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.UsePathStyle = true }) + +paginator := s3.NewListObjectsV2Paginator(client, &s3.ListObjectsV2Input{ + Bucket: aws.String(bucket), + Prefix: aws.String(namespace + "/"), +}) +var totalBytes int64 +for paginator.HasMorePages() { + page, _ := paginator.NextPage(ctx) + for _, obj := range page.Contents { + totalBytes += aws.ToInt64(obj.Size) + } +} +entry.StorageGB = float64(totalBytes) / (1024 * 1024 * 1024) +``` + +Поля `Invocations`, `CPUMilliCores`, `MemoryMB` — оставить 0 (TODO). + +### 3. `go.mod` — добавить зависимости + +``` +github.com/aws/aws-sdk-go-v2 v1.30.0 +github.com/aws/aws-sdk-go-v2/config v1.27.0 +github.com/aws/aws-sdk-go-v2/service/s3 v1.58.0 +github.com/aws/aws-sdk-go-v2/credentials v1.17.0 +``` + +Запустить `go mod tidy` после добавления. + +### 4. `deploy/admin.yaml` — добавить env в контейнер + +```yaml +env: + - name: ADMIN_TOKEN + valueFrom: + secretKeyRef: + name: fission-admin-token + key: token + - name: S3_ENDPOINT + value: "https://s3.msk-1.ngcloud.ru" + - name: S3_BUCKET + value: "sless-functions" + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: fission-s3-credentials + key: access_key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: fission-s3-credentials + key: secret_key +``` + +### 5. `ui/index.html` — доработать UI + +- В таблице "Использование" показывать реальное поле `storage_gb` (уже есть в шаблоне). +- Добавить кнопку **"Экспорт CSV"** над таблицей Usage — генерировать CSV из текущих данных и скачивать через `Blob`: + +```js +function exportCSV(rows) { + const header = 'Namespace,Email,Функции,Вызовы,Хранилище GB,Период\n'; + const body = rows.map(u => + [u.namespace, u.user_email, u.functions, u.invocations_total, + u.storage_gb.toFixed(3), `${u.period_start} — ${u.period_end}`].join(',') + ).join('\n'); + const blob = new Blob([header + body], { type: 'text/csv;charset=utf-8;' }); + const a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = `usage-${new Date().toISOString().slice(0,10)}.csv`; + a.click(); +} +``` + +--- + +## Правила среды + +- **Go version**: `1.25.0` (не менять в go.mod) +- **Образ**: `naeel/fission-admin-console:v0.1.0` +- **Build + push**: только через SSH на ВМ `naeel@5.172.178.213` (ключ у оператора) +- **Namespace деплоя**: `fission` +- **S3-секрет** `fission-s3-credentials` уже существует в namespace `fission` +- **Admin-токен** создать вручную перед деплоем: + ```bash + kubectl create secret generic fission-admin-token -n fission --from-literal=token=<случайная_строка> + ``` +- Деплой: `kubectl apply -f ~/terra/fission/admin-console/deploy/admin.yaml` + +## Что НЕ трогать + +- `client-console/` — рабочий код в проде, **не трогать вообще** +- `helm/`, `terraform/`, `scripts/` — не трогать +- Любой файл вне `admin-console/` — без явного разрешения не трогать