refactor: SoC — EnsureNamespace в namespace.go, маршрут /ensure, client.EnsureNamespace, fix secrets в .gitignore

- handler.go: убраны бизнес-логика и k8s-типы (corev1/k8serrors/metav1)
  handler.go теперь только инфраструктура: Handler struct + helpers
- namespace.go: новый файл — EnsureNamespace хендлер живёт здесь
  SoC: создание namespace — отдельная ответственность, не смешивается с CRUD
- router.go: добавлен маршрут POST /v1/namespaces/{namespace}/ensure
- client.go: добавлен метод EnsureNamespace(ctx, ns) → POST /ensure
- provider.go: Configure() вызывает c.EnsureNamespace(ctx, namespace) после создания Client
  Namespace создаётся ОДИН РАЗ при инициализации провайдера
  Resource-хендлеры (Function, Trigger, Job) namespace не трогают
- .gitignore: добавлена директория secrets/ (токены, ключи)
- provider v0.1.13, operator v0.1.21

Operator: naeel/sless-operator:v0.1.21
Provider: terra.k8c.ru/naeel/sless v0.1.13
This commit is contained in:
“Naeel”
2026-03-11 08:37:33 +04:00
parent 5ae2ee7f85
commit a1774e178f
11 changed files with 114 additions and 60 deletions
+12 -39
View File
@@ -3,26 +3,28 @@
// Все handlers получают доступ к k8s, S3 и Postgres через эту структуру.
// Логирование через slog, маршрутизация через gorilla/mux.
//
// Этот файл — чистая инфраструктура: только Handler struct + вспомогательные функции.
// Бизнес-логика по доменам — в отдельных файлах:
// - namespace.go — EnsureNamespace (создание k8s namespace)
// - functions.go — CRUD функций
// - triggers.go — CRUD триггеров
// - jobs.go — CRUD одноразовых запусков
// - upload.go — загрузка кода, сборка образа
// - invoke.go — прокси вызова функций
//
// Архитектура namespace:
// - Каждый пользователь работает в своём k8s namespace.
// - Имя namespace вычисляется провайдером из JWT-токена (SHA256 от sub).
// - Оператор сам не создаёт namespace заранее — он делает это при первом
// Create-запросе через ensureNamespace().
// - defaultNamespace используется только как fallback для dev/тестов,
// когда namespace не передан в URL.
// - Namespace создаётся ОДИН РАЗ через EnsureNamespace при инициализации провайдера.
// - Resource-хендлеры namespace не трогают — это не их ответственность.
// - defaultNamespace используется только как fallback для dev/тестов.
package handler
import (
"context"
"encoding/json"
"log/slog"
"net/http"
"github.com/gorilla/mux"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -69,32 +71,3 @@ func namespace(r *http.Request) string {
}
return defaultNamespace
}
// ensureNamespace создаёт k8s namespace если он не существует.
// Вызывается в начале каждого Create-хендлера (CreateFunction, CreateTrigger, CreateJob).
// Идемпотентен: если namespace уже есть — не ошибается.
// Зачем здесь, а не в контроллере: оператор получает запросы ДО того как
// контроллер может что-то создать, поэтому namespace нужен прямо при Create.
func (h *Handler) ensureNamespace(ctx context.Context, ns string) error {
existing := &corev1.Namespace{}
err := h.K8s.Get(ctx, client.ObjectKey{Name: ns}, existing)
if err == nil {
// namespace уже существует — всё ок
return nil
}
if !k8serrors.IsNotFound(err) {
// неожиданная ошибка k8s API
return err
}
// namespace не существует — создаём
ns_obj := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
// label для идентификации namespace как принадлежащего sless
Labels: map[string]string{
"managed-by": "sless-operator",
},
},
}
return h.K8s.Create(ctx, ns_obj)
}