- 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
58 lines
2.5 KiB
Go
58 lines
2.5 KiB
Go
// Изменено: 2026-03-11
|
||
// namespace.go — хендлер управления namespace пользователя.
|
||
// Ответственность: создание k8s namespace (один раз, при инициализации провайдера).
|
||
// Вынесен из handler.go намеренно: handler.go — базовый файл без бизнес-логики,
|
||
// а работа с corev1/metav1/k8serrors — это бизнес-логика namespace lifecycle.
|
||
//
|
||
// Точка вызова: провайдер terraform после Configure() вызывает
|
||
// POST /v1/namespaces/{namespace}/ensure ОДИН РАЗ перед созданием любых ресурсов.
|
||
// Resource-хендлеры (functions, triggers, jobs) namespace НЕ трогают.
|
||
|
||
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
corev1 "k8s.io/api/core/v1"
|
||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||
)
|
||
|
||
// EnsureNamespace — хендлер POST /v1/namespaces/{namespace}/ensure.
|
||
// Создаёт k8s namespace если не существует. Идемпотентен: если namespace уже есть —
|
||
// возвращает 200 OK вместо 201 Created.
|
||
// Вызывается провайдером ОДИН РАЗ в Configure() — до создания любых ресурсов.
|
||
func (h *Handler) EnsureNamespace(w http.ResponseWriter, r *http.Request) {
|
||
ns := namespace(r)
|
||
existing := &corev1.Namespace{}
|
||
err := h.K8s.Get(r.Context(), client.ObjectKey{Name: ns}, existing)
|
||
if err == nil {
|
||
// namespace уже существует
|
||
writeJSON(w, http.StatusOK, map[string]string{"namespace": ns, "status": "exists"})
|
||
return
|
||
}
|
||
if !k8serrors.IsNotFound(err) {
|
||
writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
|
||
return
|
||
}
|
||
nsObj := &corev1.Namespace{
|
||
ObjectMeta: metav1.ObjectMeta{
|
||
Name: ns,
|
||
Labels: map[string]string{
|
||
"managed-by": "sless-operator",
|
||
},
|
||
},
|
||
}
|
||
if err := h.K8s.Create(r.Context(), nsObj); err != nil {
|
||
// параллельный запрос уже создал namespace — идемпотентность
|
||
if k8serrors.IsAlreadyExists(err) {
|
||
writeJSON(w, http.StatusOK, map[string]string{"namespace": ns, "status": "exists"})
|
||
return
|
||
}
|
||
writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
|
||
return
|
||
}
|
||
writeJSON(w, http.StatusCreated, map[string]string{"namespace": ns, "status": "created"})
|
||
}
|