129 lines
5.4 KiB
Go
129 lines
5.4 KiB
Go
// Package fission — чистый адаптер для Fission CRD API.
|
||
// Содержит только то что специфично для Fission и не зависит от облачной платформы:
|
||
// - SetupFissionNamespace: создание NS + label для Layer 1 NSWatcher + Fission ServiceAccounts + RoleBindings
|
||
// - EnsureEnvironment, CleanupEnvironmentIfUnused: управление Fission Environment CRD
|
||
// - GVR константы (client.go)
|
||
//
|
||
// Облачно-специфичные вещи (ResourceQuota, LimitRange, NetworkPolicy, NSManager) — в пакете cloud.
|
||
package fission
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"strings"
|
||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
"k8s.io/client-go/dynamic"
|
||
)
|
||
|
||
// SetupFissionNamespace создаёт namespace с нужными метками и регистрирует в нём
|
||
// все Fission ServiceAccounts и RoleBindings. Идемпотентно.
|
||
//
|
||
// Метки на Namespace:
|
||
// - managed-by=fission-console — для фильтрации наших NS в cloud-слое
|
||
// - fission.io/managed=true — для Layer 1 NSWatcher в executor (auto-discovery)
|
||
//
|
||
// ServiceAccounts:
|
||
// - fission-fetcher, fission-builder — нужны Fission pool pods в user NS
|
||
//
|
||
// RoleBindings (cluster-admin в рамках NS):
|
||
// - Для всех Fission system SA из FISSION_SYSTEM_NAMESPACE (default: fission)
|
||
// - Почему cluster-admin, а не admin: ClusterRole "admin" не включает fission.io/* CRD-группы,
|
||
// executor получает "RBAC escalation" при создании Role. cluster-admin в RoleBinding
|
||
// (не ClusterRoleBinding) безопасен — даёт полный доступ только внутри NS.
|
||
func SetupFissionNamespace(ctx context.Context, dyn dynamic.Interface, ns string) error {
|
||
// 1. Namespace
|
||
labels := map[string]any{
|
||
"managed-by": "fission-console",
|
||
"fission.io/managed": "true", // Layer 1: executor NSWatcher auto-discovers this NS
|
||
}
|
||
if strings.HasPrefix(ns, "fission-test-") {
|
||
labels["fission-console/env"] = "test"
|
||
}
|
||
nsObj := &unstructured.Unstructured{
|
||
Object: map[string]any{
|
||
"apiVersion": "v1",
|
||
"kind": "Namespace",
|
||
"metadata": map[string]any{
|
||
"name": ns,
|
||
"labels": labels,
|
||
},
|
||
},
|
||
}
|
||
_, err := dyn.Resource(NamespaceGVR).Create(ctx, nsObj, metav1.CreateOptions{})
|
||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||
return fmt.Errorf("create namespace %s: %w", ns, err)
|
||
}
|
||
|
||
// 2. ServiceAccounts для Fission pool pods в user namespace.
|
||
// Fetcher sidecar требует fission-fetcher SA в том же NS где запускается.
|
||
saGVR := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "serviceaccounts"}
|
||
for _, saName := range []string{"fission-fetcher", "fission-builder"} {
|
||
saObj := &unstructured.Unstructured{Object: map[string]any{
|
||
"apiVersion": "v1",
|
||
"kind": "ServiceAccount",
|
||
"metadata": map[string]any{"name": saName, "namespace": ns},
|
||
}}
|
||
_, saErr := dyn.Resource(saGVR).Namespace(ns).Create(ctx, saObj, metav1.CreateOptions{})
|
||
if saErr != nil && !apierrors.IsAlreadyExists(saErr) {
|
||
log.Printf("fission.SetupFissionNamespace: create SA %s/%s: %v", ns, saName, saErr)
|
||
}
|
||
}
|
||
|
||
// 3. RoleBindings для всех Fission system SA.
|
||
fissionSysNS := os.Getenv("FISSION_SYSTEM_NAMESPACE")
|
||
if fissionSysNS == "" {
|
||
fissionSysNS = "fission"
|
||
}
|
||
type rbDef struct {
|
||
name string
|
||
namespace string
|
||
binding string
|
||
}
|
||
bindings := []rbDef{
|
||
{name: "fission-executor", binding: "fission-executor-user-ns"},
|
||
{name: "fission-router", binding: "fission-router-user-ns"},
|
||
{name: "fission-buildermgr", binding: "fission-buildermgr-user-ns"},
|
||
{name: "fission-kubewatcher", binding: "fission-kubewatcher-user-ns"},
|
||
{name: "fission-timer", binding: "fission-timer-user-ns"},
|
||
{name: "fission-fetcher", binding: "fission-fetcher-system-user-ns"},
|
||
{name: "fission-builder", binding: "fission-builder-system-user-ns"},
|
||
// fetcher/builder также нужны локально (запускаются в user NS)
|
||
{name: "fission-fetcher", namespace: ns, binding: "fission-fetcher-local-user-ns"},
|
||
{name: "fission-builder", namespace: ns, binding: "fission-builder-local-user-ns"},
|
||
}
|
||
rbGVR := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}
|
||
for _, rb := range bindings {
|
||
subjectNS := rb.namespace
|
||
if subjectNS == "" {
|
||
subjectNS = fissionSysNS
|
||
}
|
||
rbObj := &unstructured.Unstructured{
|
||
Object: map[string]any{
|
||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||
"kind": "RoleBinding",
|
||
"metadata": map[string]any{"name": rb.binding, "namespace": ns},
|
||
"roleRef": map[string]any{
|
||
"apiGroup": "rbac.authorization.k8s.io",
|
||
"kind": "ClusterRole",
|
||
"name": "cluster-admin",
|
||
},
|
||
"subjects": []any{map[string]any{
|
||
"kind": "ServiceAccount", "name": rb.name, "namespace": subjectNS,
|
||
}},
|
||
},
|
||
}
|
||
_, rbErr := dyn.Resource(rbGVR).Namespace(ns).Create(ctx, rbObj, metav1.CreateOptions{})
|
||
if rbErr != nil && !apierrors.IsAlreadyExists(rbErr) {
|
||
log.Printf("fission.SetupFissionNamespace: create rolebinding %s/%s@%s: %v", ns, rb.name, subjectNS, rbErr)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|