- operator: ensureNamespace() создаёт k8s namespace при первом Create-запросе
- operator: defaultNamespace константа вместо хардкода 'default'
- provider: SubFromJWT декодирует JWT payload, извлекает sub
- provider: NamespaceFromSub вычисляет sless-{sha256[:8]} из sub
- provider: PingNubesAPI валидирует токен запросом к nubes API
- provider: Configure вычисляет namespace и создаёт Client с ним
- provider: новый атрибут nubes_endpoint (опционально, env: NUBES_ENDPOINT)
101 lines
4.0 KiB
Go
101 lines
4.0 KiB
Go
// Изменено: 2026-03-11
|
||
// Handler — общий контейнер зависимостей для всех REST handlers.
|
||
// Все handlers получают доступ к k8s, S3 и Postgres через эту структуру.
|
||
// Логирование через slog, маршрутизация через gorilla/mux.
|
||
//
|
||
// Архитектура namespace:
|
||
// - Каждый пользователь работает в своём k8s namespace.
|
||
// - Имя namespace вычисляется провайдером из JWT-токена (SHA256 от sub).
|
||
// - Оператор сам не создаёт namespace заранее — он делает это при первом
|
||
// Create-запросе через ensureNamespace().
|
||
// - defaultNamespace используется только как fallback для dev/тестов,
|
||
// когда namespace не передан в URL.
|
||
|
||
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"
|
||
|
||
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/postgres"
|
||
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/storage/s3"
|
||
)
|
||
|
||
// defaultNamespace — fallback namespace для dev/тестов.
|
||
// В production namespace определяется из JWT-токена провайдером.
|
||
// Все обращения к "default" строке идут через эту константу — одно место замены.
|
||
const defaultNamespace = "default"
|
||
|
||
// Handler содержит зависимости для всех REST-обработчиков.
|
||
type Handler struct {
|
||
K8s client.Client
|
||
Scheme *runtime.Scheme
|
||
S3 *s3.Client
|
||
PG *postgres.Store
|
||
Log *slog.Logger
|
||
}
|
||
|
||
// writeJSON отправляет JSON-ответ с указанным статусом.
|
||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(status)
|
||
_ = json.NewEncoder(w).Encode(v)
|
||
}
|
||
|
||
// errResp возвращает структуру ошибки для JSON.
|
||
func errResp(msg string) map[string]string {
|
||
return map[string]string{"error": msg}
|
||
}
|
||
|
||
// pathVar читает переменную пути из gorilla/mux.
|
||
func pathVar(r *http.Request, key string) string {
|
||
return mux.Vars(r)[key]
|
||
}
|
||
|
||
// namespace читает {namespace} из пути URL.
|
||
// Fallback — defaultNamespace (используется только в dev/тестах).
|
||
func namespace(r *http.Request) string {
|
||
if ns := mux.Vars(r)["namespace"]; ns != "" {
|
||
return ns
|
||
}
|
||
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)
|
||
}
|