feat: Fission SA RoleBindings in user namespace (cluster-admin) + v0.6.2
This commit is contained in:
+132
@@ -27,6 +27,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -39,6 +40,7 @@ var (
|
|||||||
httpTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "httptriggers"}
|
httpTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "httptriggers"}
|
||||||
timeTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "timetriggers"}
|
timeTrigGVR = schema.GroupVersionResource{Group: "fission.io", Version: "v1", Resource: "timetriggers"}
|
||||||
namespaceGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
|
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"
|
const defaultSATokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||||
@@ -798,6 +800,92 @@ func namespaceFromJWT(token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensureUserNamespace создаёт K8s namespace и shared environments если не существуют.
|
// 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 {
|
func (s *server) ensureUserNamespace(ctx context.Context, ns string) error {
|
||||||
// 1. Создать namespace
|
// 1. Создать namespace
|
||||||
nsObj := &unstructured.Unstructured{
|
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{})
|
_, err := s.dyn.Resource(namespaceGVR).Create(ctx, nsObj, metav1.CreateOptions{})
|
||||||
|
newlyCreated := err == nil
|
||||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||||
return fmt.Errorf("create namespace %s: %w", ns, 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 для всех языков
|
// 2. Создать shared environments для всех языков
|
||||||
for lang, def := range langEnvMap {
|
for lang, def := range langEnvMap {
|
||||||
envName := lang + "-env"
|
envName := lang + "-env"
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ rules:
|
|||||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
resources: ["rolebindings"]
|
resources: ["rolebindings"]
|
||||||
verbs: ["get", "create"]
|
verbs: ["get", "create"]
|
||||||
|
- apiGroups: ["apps"]
|
||||||
|
resources: ["deployments"]
|
||||||
|
verbs: ["get", "patch"]
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
|
|||||||
Reference in New Issue
Block a user