From 8a520cdcc27da401b5676c23e56341eea591fad1 Mon Sep 17 00:00:00 2001 From: Naeel Date: Sun, 19 Apr 2026 14:40:03 +0300 Subject: [PATCH] feat: Fission SA RoleBindings in user namespace (cluster-admin) + v0.6.2 --- console/main.go | 132 ++++++++++++++++++++++++++++ deploy/rbac/console-ns-manager.yaml | 3 + 2 files changed, 135 insertions(+) diff --git a/console/main.go b/console/main.go index 8ae223b..b5614fd 100644 --- a/console/main.go +++ b/console/main.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -39,6 +40,7 @@ var ( httpTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "httptriggers"} timeTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "timetriggers"} namespaceGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"} + deploymentGVR = schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} ) const defaultSATokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" @@ -798,6 +800,92 @@ func namespaceFromJWT(token string) (string, error) { } // ensureUserNamespace создаёт K8s namespace и shared environments если не существуют. +// addNSToFission dynamically adds ns to FISSION_RESOURCE_NAMESPACES on all Fission deployments. +func (s *server) addNSToFission(ctx context.Context, ns string) error { + fissionNS := os.Getenv("FISSION_SYSTEM_NAMESPACE") + if fissionNS == "" { + fissionNS = "fission" + } + fissionDeployments := []string{"router", "executor", "buildermgr", "kubewatcher", "timer"} + + // Читаем текущее значение из router + routerDep, err := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Get(ctx, "router", metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("get router deployment: %w", err) + } + + currentVal := "default" + containerName := "router" + containers, _, _ := unstructured.NestedSlice(routerDep.Object, "spec", "template", "spec", "containers") + for _, c := range containers { + cont, ok := c.(map[string]any) + if !ok { + continue + } + if n, ok := cont["name"].(string); ok { + containerName = n + } + envs, _, _ := unstructured.NestedSlice(cont, "env") + for _, e := range envs { + env, ok := e.(map[string]any) + if !ok { + continue + } + if env["name"] == "FISSION_RESOURCE_NAMESPACES" { + if v, ok := env["value"].(string); ok && v != "" { + currentVal = v + } + } + } + break + } + + // Проверяем что namespace ещё не в списке + for _, existing := range strings.Split(currentVal, ",") { + if strings.TrimSpace(existing) == ns { + return nil // уже есть + } + } + newVal := currentVal + "," + ns + + // Патчим все Fission deployments + patch := map[string]any{ + "spec": map[string]any{ + "template": map[string]any{ + "spec": map[string]any{ + "containers": []any{ + map[string]any{ + "name": containerName, + "env": []any{ + map[string]any{ + "name": "FISSION_RESOURCE_NAMESPACES", + "value": newVal, + }, + }, + }, + }, + }, + }, + }, + } + patchBytes, err := json.Marshal(patch) + if err != nil { + return fmt.Errorf("marshal patch: %w", err) + } + for _, dep := range fissionDeployments { + // container name совпадает с deployment name в fission + patch["spec"].(map[string]any)["template"].(map[string]any)["spec"].(map[string]any)["containers"].([]any)[0].(map[string]any)["name"] = dep + patchBytes, _ = json.Marshal(patch) + _, patchErr := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Patch( + ctx, dep, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) + if patchErr != nil { + log.Printf("addNSToFission: patch deployment %s: %v", dep, patchErr) + } + } + log.Printf("addNSToFission: added %s, new list: %s", ns, newVal) + return nil +} + func (s *server) ensureUserNamespace(ctx context.Context, ns string) error { // 1. Создать namespace nsObj := &unstructured.Unstructured{ @@ -813,10 +901,54 @@ func (s *server) ensureUserNamespace(ctx context.Context, ns string) error { }, } _, err := s.dyn.Resource(namespaceGVR).Create(ctx, nsObj, metav1.CreateOptions{}) + newlyCreated := err == nil if err != nil && !apierrors.IsAlreadyExists(err) { return fmt.Errorf("create namespace %s: %w", ns, err) } + // 1b. Создаём RoleBindings для Fission SA (всегда — idempotent через IsAlreadyExists) + fissionSAs := []string{"fission-executor", "fission-router", "fission-buildermgr", "fission-kubewatcher", "fission-timer", "fission-fetcher", "fission-builder"} + fissionSysNS := os.Getenv("FISSION_SYSTEM_NAMESPACE") + if fissionSysNS == "" { + fissionSysNS = "fission" + } + for _, sa := range fissionSAs { + rbObj := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "RoleBinding", + "metadata": map[string]any{ + "name": "fission-" + sa + "-user-ns", + "namespace": ns, + }, + "roleRef": map[string]any{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "ClusterRole", + "name": "cluster-admin", + }, + "subjects": []any{ + map[string]any{ + "kind": "ServiceAccount", + "name": sa, + "namespace": fissionSysNS, + }, + }, + }, + } + rbGVR := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"} + _, rbErr := s.dyn.Resource(rbGVR).Namespace(ns).Create(ctx, rbObj, metav1.CreateOptions{}) + if rbErr != nil && !apierrors.IsAlreadyExists(rbErr) { + log.Printf("ensureUserNamespace: create rolebinding %s/%s: %v", ns, sa, rbErr) + } + } + + // 1c. Патчим Fission если namespace новый + if newlyCreated { + if patchErr := s.addNSToFission(ctx, ns); patchErr != nil { + log.Printf("ensureUserNamespace: addNSToFission: %v", patchErr) + } + } + // 2. Создать shared environments для всех языков for lang, def := range langEnvMap { envName := lang + "-env" diff --git a/deploy/rbac/console-ns-manager.yaml b/deploy/rbac/console-ns-manager.yaml index 7bc235b..4acd5b8 100644 --- a/deploy/rbac/console-ns-manager.yaml +++ b/deploy/rbac/console-ns-manager.yaml @@ -12,6 +12,9 @@ rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["get", "create"] +- apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding