feat: patch Deployment env vars directly via k8s API (v1.3.78)
Fission newdeploy executor does NOT apply fn.Spec.PodSpec when building Deployment spec - it only uses env.Spec.Runtime.Container and podSpecPatch. Fix: after updating Function CRD, also directly patch the newdeploy Deployment via Kubernetes API using StrategicMergePatch, searching by labels functionName/functionNamespace/executorType=newdeploy. This ensures env vars immediately appear in the container and survive pod scaling events (scale to 0 / scale back to 1).
This commit is contained in:
@@ -55,7 +55,7 @@ spec:
|
|||||||
serviceAccountName: fission-console
|
serviceAccountName: fission-console
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: naeel/fission-console:v1.3.77
|
image: naeel/fission-console:v1.3.78
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8090
|
- containerPort: 8090
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
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"
|
||||||
|
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleGetFunction возвращает детали функции: код, environment, route, methods.
|
// handleGetFunction возвращает детали функции: код, environment, route, methods.
|
||||||
@@ -395,9 +396,9 @@ func (s *Server) handlePutFunctionEnvVars(w http.ResponseWriter, r *http.Request
|
|||||||
// Переключаем на newdeploy (только он поддерживает podspec env)
|
// Переключаем на newdeploy (только он поддерживает podspec env)
|
||||||
if err := unstructured.SetNestedField(fn.Object, map[string]any{
|
if err := unstructured.SetNestedField(fn.Object, map[string]any{
|
||||||
"ExecutionStrategy": map[string]any{
|
"ExecutionStrategy": map[string]any{
|
||||||
"ExecutorType": "newdeploy",
|
"ExecutorType": "newdeploy",
|
||||||
"MinScale": int64(0),
|
"MinScale": int64(0),
|
||||||
"MaxScale": int64(1),
|
"MaxScale": int64(1),
|
||||||
"SpecializationTimeout": int64(120),
|
"SpecializationTimeout": int64(120),
|
||||||
},
|
},
|
||||||
"StrategyType": "execution",
|
"StrategyType": "execution",
|
||||||
@@ -426,6 +427,18 @@ func (s *Server) handlePutFunctionEnvVars(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fission newdeploy НЕ применяет fn.Spec.PodSpec при построении Deployment.
|
||||||
|
// Поэтому напрямую патчим существующий Deployment через Kubernetes API.
|
||||||
|
if len(req.EnvVars) > 0 && s.kube != nil {
|
||||||
|
envName, _, _ := unstructured.NestedString(fn.Object, "spec", "environment", "name")
|
||||||
|
if envName != "" {
|
||||||
|
if err := s.patchDeploymentEnvVars(ctx, ns, name, envName, req.EnvVars); err != nil {
|
||||||
|
// Не фатальная ошибка — CRD обновлён, Deployment будет обновлён позже
|
||||||
|
s.log.Sugar().Warnf("patch deployment env vars: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
executor := "poolmgr"
|
executor := "poolmgr"
|
||||||
if len(req.EnvVars) > 0 {
|
if len(req.EnvVars) > 0 {
|
||||||
executor = "newdeploy"
|
executor = "newdeploy"
|
||||||
@@ -450,3 +463,51 @@ func extractEnvVars(fn *unstructured.Unstructured) []map[string]string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// patchDeploymentEnvVars находит Deployment newdeploy для функции и патчит его env vars.
|
||||||
|
// Fission не применяет fn.Spec.PodSpec при построении Deployment, поэтому патчим напрямую.
|
||||||
|
// Поиск по labels: functionName=<name>, functionNamespace=<ns>, executorType=newdeploy
|
||||||
|
func (s *Server) patchDeploymentEnvVars(ctx context.Context, ns, fnName, envContainerName string, envVars []map[string]string) error {
|
||||||
|
selector := fmt.Sprintf("functionName=%s,functionNamespace=%s,executorType=newdeploy", fnName, ns)
|
||||||
|
deplList, err := s.kube.AppsV1().Deployments(ns).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("list deployments: %w", err)
|
||||||
|
}
|
||||||
|
if len(deplList.Items) == 0 {
|
||||||
|
return nil // Deployment ещё не создан Fission — ничего страшного
|
||||||
|
}
|
||||||
|
|
||||||
|
// Строим env vars для patch (StrategicMergePatch мержит по "name")
|
||||||
|
envItems := make([]map[string]string, 0, len(envVars))
|
||||||
|
for _, ev := range envVars {
|
||||||
|
envItems = append(envItems, map[string]string{"name": ev["name"], "value": ev["value"]})
|
||||||
|
}
|
||||||
|
|
||||||
|
patch := map[string]any{
|
||||||
|
"spec": map[string]any{
|
||||||
|
"template": map[string]any{
|
||||||
|
"spec": map[string]any{
|
||||||
|
"containers": []any{
|
||||||
|
map[string]any{
|
||||||
|
"name": envContainerName,
|
||||||
|
"env": envItems,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
patchBytes, err := json.Marshal(patch)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal patch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, depl := range deplList.Items {
|
||||||
|
if _, err := s.kube.AppsV1().Deployments(ns).Patch(
|
||||||
|
ctx, depl.Name, k8stypes.StrategicMergePatchType, patchBytes, metav1.PatchOptions{},
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("patch deployment %s: %w", depl.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
<div class="nubes">NUBES</div>
|
<div class="nubes">NUBES</div>
|
||||||
<div class="product">FISSION CONSOLE</div>
|
<div class="product">FISSION CONSOLE</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.77</div>
|
<div style="font-size:0.65rem; color:var(--text-secondary); margin-left:10px; align-self:center; opacity:0.7;">v1.3.78</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin:0;">
|
<div class="row" style="margin:0;">
|
||||||
<button class="btn ghost" onclick="reloadAll()">Refresh</button>
|
<button class="btn ghost" onclick="reloadAll()">Refresh</button>
|
||||||
@@ -510,7 +510,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions" style="justify-content:space-between; align-items:center;">
|
<div class="actions" style="justify-content:space-between; align-items:center;">
|
||||||
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.77</span>
|
<span style="font-size:0.75rem; color:var(--text-secondary);">v1.3.78</span>
|
||||||
<button class="btn ghost" onclick="closeHelp()">Закрыть</button>
|
<button class="btn ghost" onclick="closeHelp()">Закрыть</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user