diff --git a/charts/flowise/ci/queue-s3.yaml b/charts/flowise/ci/queue-s3.yaml index 94ca01549..658477b12 100644 --- a/charts/flowise/ci/queue-s3.yaml +++ b/charts/flowise/ci/queue-s3.yaml @@ -3,7 +3,7 @@ architecture: mode: queue flowise: - replicaCount: 2 + replicaCount: 1 persistence: enabled: false diff --git a/charts/flowise/templates/_helpers.tpl b/charts/flowise/templates/_helpers.tpl index f57a368ea..23cb599c8 100644 --- a/charts/flowise/templates/_helpers.tpl +++ b/charts/flowise/templates/_helpers.tpl @@ -248,11 +248,11 @@ redis-password {{- end -}} {{- define "flowise.mainCommand" -}} -sleep 3; flowise start +sleep {{ int .Values.flowise.startupDelaySeconds }}; flowise start {{- end -}} {{- define "flowise.workerCommand" -}} -sleep 3; flowise worker +sleep {{ int .Values.queue.worker.startupDelaySeconds }}; flowise worker {{- end -}} {{- define "flowise.backupSecretName" -}} @@ -281,3 +281,54 @@ sleep 3; flowise worker true {{- end -}} {{- end -}} + +{{- define "flowise.validate" -}} +{{- $arch := include "flowise.architectureMode" . -}} +{{- $dbMode := include "flowise.databaseMode" . -}} +{{- if and (eq $arch "queue") (eq $dbMode "sqlite") -}} +{{- fail "architecture.mode=queue requires database.mode to resolve to external or postgresql, not sqlite" -}} +{{- end -}} +{{- if and (eq $arch "queue") (eq (include "flowise.redisMode" .) "none") -}} +{{- fail "architecture.mode=queue requires Redis: enable redis.enabled or configure redis.external.host" -}} +{{- end -}} +{{- if and (eq $arch "queue") (ne .Values.storage.type "s3") -}} +{{- fail "architecture.mode=queue requires storage.type=s3 for shared blob storage across main and worker pods" -}} +{{- end -}} +{{- if and (eq $arch "queue") .Values.persistence.enabled -}} +{{- fail "architecture.mode=queue requires persistence.enabled=false because shared local volumes are not a valid scalable topology for Flowise" -}} +{{- end -}} +{{- if and (eq $dbMode "sqlite") (gt (int .Values.flowise.replicaCount) 1) -}} +{{- fail "SQLite mode only supports flowise.replicaCount=1" -}} +{{- end -}} +{{- if and (eq .Values.storage.type "local") (gt (int .Values.flowise.replicaCount) 1) -}} +{{- fail "local storage only supports flowise.replicaCount=1; use storage.type=s3 for multiple replicas" -}} +{{- end -}} +{{- if and (eq .Values.storage.type "s3") (eq (.Values.storage.s3.bucketName | default "") "") -}} +{{- fail "storage.type=s3 requires storage.s3.bucketName" -}} +{{- end -}} +{{- if and (eq .Values.storage.type "s3") (not (or .Values.storage.s3.existingSecret (and .Values.storage.s3.accessKeyId .Values.storage.s3.secretAccessKey))) -}} +{{- fail "storage.type=s3 requires storage.s3.existingSecret or inline storage.s3.accessKeyId and storage.s3.secretAccessKey" -}} +{{- end -}} +{{- $podLabels := .Values.podLabels | default dict -}} +{{- if hasKey $podLabels "app.kubernetes.io/name" -}} +{{- fail "podLabels must not override the selector label app.kubernetes.io/name" -}} +{{- end -}} +{{- if hasKey $podLabels "app.kubernetes.io/instance" -}} +{{- fail "podLabels must not override the selector label app.kubernetes.io/instance" -}} +{{- end -}} +{{- if hasKey $podLabels "app.kubernetes.io/component" -}} +{{- fail "podLabels must not override the selector label app.kubernetes.io/component" -}} +{{- end -}} +{{- if eq $arch "queue" -}} +{{- $workerPodLabels := .Values.queue.worker.podLabels | default dict -}} +{{- if hasKey $workerPodLabels "app.kubernetes.io/name" -}} +{{- fail "queue.worker.podLabels must not override the selector label app.kubernetes.io/name" -}} +{{- end -}} +{{- if hasKey $workerPodLabels "app.kubernetes.io/instance" -}} +{{- fail "queue.worker.podLabels must not override the selector label app.kubernetes.io/instance" -}} +{{- end -}} +{{- if hasKey $workerPodLabels "app.kubernetes.io/component" -}} +{{- fail "queue.worker.podLabels must not override the selector label app.kubernetes.io/component" -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/flowise/templates/validate.yaml b/charts/flowise/templates/validate.yaml index afaf7c86b..7ff374ac7 100644 --- a/charts/flowise/templates/validate.yaml +++ b/charts/flowise/templates/validate.yaml @@ -1,27 +1,2 @@ -{{/* SPDX-License-Identifier: Apache-2.0 */}} -{{- $arch := include "flowise.architectureMode" . -}} -{{- $dbMode := include "flowise.databaseMode" . -}} -{{- if and (eq $arch "queue") (eq $dbMode "sqlite") -}} -{{- fail "architecture.mode=queue requires database.mode to resolve to external or postgresql, not sqlite" -}} -{{- end -}} -{{- if and (eq $arch "queue") (eq (include "flowise.redisMode" .) "none") -}} -{{- fail "architecture.mode=queue requires Redis: enable redis.enabled or configure redis.external.host" -}} -{{- end -}} -{{- if and (eq $arch "queue") (ne .Values.storage.type "s3") -}} -{{- fail "architecture.mode=queue requires storage.type=s3 for shared blob storage across main and worker pods" -}} -{{- end -}} -{{- if and (eq $arch "queue") .Values.persistence.enabled -}} -{{- fail "architecture.mode=queue requires persistence.enabled=false because shared local volumes are not a valid scalable topology for Flowise" -}} -{{- end -}} -{{- if and (eq $dbMode "sqlite") (gt (int .Values.flowise.replicaCount) 1) -}} -{{- fail "SQLite mode only supports flowise.replicaCount=1" -}} -{{- end -}} -{{- if and (eq .Values.storage.type "local") (gt (int .Values.flowise.replicaCount) 1) -}} -{{- fail "local storage only supports flowise.replicaCount=1; use storage.type=s3 for multiple replicas" -}} -{{- end -}} -{{- if and (eq .Values.storage.type "s3") (eq (.Values.storage.s3.bucketName | default "") "") -}} -{{- fail "storage.type=s3 requires storage.s3.bucketName" -}} -{{- end -}} -{{- if and (eq .Values.storage.type "s3") (not (or .Values.storage.s3.existingSecret (and .Values.storage.s3.accessKeyId .Values.storage.s3.secretAccessKey))) -}} -{{- fail "storage.type=s3 requires storage.s3.existingSecret or inline storage.s3.accessKeyId and storage.s3.secretAccessKey" -}} -{{- end -}} +# SPDX-License-Identifier: Apache-2.0 +{{- include "flowise.validate" . -}} diff --git a/charts/flowise/tests/deployment_test.yaml b/charts/flowise/tests/deployment_test.yaml index 1d92b044e..84aca6b61 100644 --- a/charts/flowise/tests/deployment_test.yaml +++ b/charts/flowise/tests/deployment_test.yaml @@ -28,6 +28,15 @@ tests: path: spec.template.spec.containers[0].args content: sleep 3; flowise start + - it: supports configurable startup delay for the main deployment + template: templates/deployment.yaml + set: + flowise.startupDelaySeconds: 12 + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: sleep 12; flowise start + - it: renders queue mode with redis and postgresql settings on the main deployment template: templates/deployment.yaml set: diff --git a/charts/flowise/tests/validation_test.yaml b/charts/flowise/tests/validation_test.yaml new file mode 100644 index 000000000..9ef206125 --- /dev/null +++ b/charts/flowise/tests/validation_test.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: Apache-2.0 +suite: validation +templates: + - templates/validate.yaml +tests: + - it: passes with default values + asserts: + - hasDocuments: + count: 0 + + - it: fails when queue mode resolves to sqlite + set: + architecture.mode: queue + asserts: + - failedTemplate: + errorMessage: architecture.mode=queue requires database.mode to resolve to external or postgresql, not sqlite + + - it: fails when queue mode has no Redis + set: + architecture.mode: queue + postgresql.enabled: true + persistence.enabled: false + storage.type: s3 + storage.s3.bucketName: flowise + storage.s3.existingSecret: flowise-s3 + asserts: + - failedTemplate: + errorMessage: "architecture.mode=queue requires Redis: enable redis.enabled or configure redis.external.host" + + - it: fails when queue mode does not use S3 storage + set: + architecture.mode: queue + postgresql.enabled: true + redis.enabled: true + persistence.enabled: false + storage.type: local + asserts: + - failedTemplate: + errorMessage: architecture.mode=queue requires storage.type=s3 for shared blob storage across main and worker pods + + - it: fails when queue mode leaves local persistence enabled + set: + architecture.mode: queue + postgresql.enabled: true + redis.enabled: true + storage.type: s3 + storage.s3.bucketName: flowise + storage.s3.existingSecret: flowise-s3 + persistence.enabled: true + asserts: + - failedTemplate: + errorMessage: architecture.mode=queue requires persistence.enabled=false because shared local volumes are not a valid scalable topology for Flowise + + - it: fails when sqlite has multiple replicas + set: + flowise.replicaCount: 2 + asserts: + - failedTemplate: + errorMessage: SQLite mode only supports flowise.replicaCount=1 + + - it: fails when local storage has multiple replicas + set: + database.mode: external + database.external.host: postgres.example.com + flowise.replicaCount: 2 + storage.type: local + asserts: + - failedTemplate: + errorMessage: local storage only supports flowise.replicaCount=1; use storage.type=s3 for multiple replicas + + - it: fails when S3 storage has no bucket + set: + storage.type: s3 + storage.s3.existingSecret: flowise-s3 + asserts: + - failedTemplate: + errorMessage: storage.type=s3 requires storage.s3.bucketName + + - it: fails when S3 storage has no credentials + set: + storage.type: s3 + storage.s3.bucketName: flowise + storage.s3.existingSecret: "" + storage.s3.accessKeyId: "" + storage.s3.secretAccessKey: "" + asserts: + - failedTemplate: + errorMessage: storage.type=s3 requires storage.s3.existingSecret or inline storage.s3.accessKeyId and storage.s3.secretAccessKey + + - it: fails when podLabels override selector labels + set: + podLabels: + app.kubernetes.io/name: custom + asserts: + - failedTemplate: + errorMessage: podLabels must not override the selector label app.kubernetes.io/name + + - it: fails when worker podLabels override selector labels + set: + architecture.mode: queue + persistence.enabled: false + storage.type: s3 + storage.s3.bucketName: flowise + storage.s3.existingSecret: flowise-s3 + postgresql.enabled: true + redis.enabled: true + queue.worker.podLabels: + app.kubernetes.io/component: custom + asserts: + - failedTemplate: + errorMessage: queue.worker.podLabels must not override the selector label app.kubernetes.io/component + + - it: allows worker podLabels in standalone mode because no worker is rendered + set: + queue.worker.podLabels: + app.kubernetes.io/component: custom + asserts: + - hasDocuments: + count: 0 diff --git a/charts/flowise/tests/worker-deployment_test.yaml b/charts/flowise/tests/worker-deployment_test.yaml index 7a3fb5fa4..240670f60 100644 --- a/charts/flowise/tests/worker-deployment_test.yaml +++ b/charts/flowise/tests/worker-deployment_test.yaml @@ -29,7 +29,7 @@ tests: value: 3 - contains: path: spec.template.spec.containers[0].args - content: sleep 3; flowise worker + content: sleep 30; flowise worker - contains: path: spec.template.spec.containers[0].env content: @@ -40,3 +40,20 @@ tests: content: name: WORKER_CONCURRENCY value: "100000" + + - it: supports configurable startup delay for workers + template: templates/worker-deployment.yaml + set: + architecture.mode: queue + persistence.enabled: false + storage.type: s3 + storage.s3.bucketName: flowise + storage.s3.accessKeyId: minio + storage.s3.secretAccessKey: secret123 + postgresql.enabled: true + redis.enabled: true + queue.worker.startupDelaySeconds: 45 + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: sleep 45; flowise worker diff --git a/charts/flowise/values.schema.json b/charts/flowise/values.schema.json index f8caae17e..23473bd24 100644 --- a/charts/flowise/values.schema.json +++ b/charts/flowise/values.schema.json @@ -30,6 +30,7 @@ "description": "Main Flowise application settings", "properties": { "replicaCount": { "type": "integer", "description": "Number of main Flowise server replicas" }, + "startupDelaySeconds": { "type": "integer", "minimum": 0, "description": "Delay before starting the main Flowise server, in seconds" }, "appUrl": { "type": "string", "description": "Public application URL. Auto-detected from the first ingress host when empty." }, "logLevel": { "type": "string", "description": "Log level: error | warn | info | verbose | debug" }, "numberOfProxies": { "type": "integer", "description": "Rate-limit proxy count when running behind ingress or load balancers" }, @@ -53,7 +54,32 @@ } }, "postgresql": { "type": "object", "description": "PostgreSQL subchart configuration" }, - "queue": { "type": "object", "description": "Queue mode settings including worker replicas and concurrency" }, + "queue": { + "type": "object", + "description": "Queue mode settings including worker replicas and concurrency", + "properties": { + "name": { "type": "string", "description": "Queue name used by Flowise main and workers" }, + "worker": { + "type": "object", + "description": "Flowise worker settings used in queue mode", + "properties": { + "replicaCount": { "type": "integer", "minimum": 0, "description": "Number of Flowise worker replicas in queue mode" }, + "startupDelaySeconds": { + "type": "integer", + "minimum": 0, + "description": "Delay before starting workers, in seconds" + }, + "concurrency": { "type": "integer", "minimum": 0, "description": "Worker concurrency for BullMQ jobs" }, + "removeOnAge": { "type": "integer", "minimum": 0, "description": "Number of completed jobs kept by age" }, + "removeOnCount": { "type": "integer", "minimum": 0, "description": "Number of completed jobs kept by count" }, + "podLabels": { "type": "object", "description": "Labels added to Flowise worker pods" }, + "podAnnotations": { "type": "object", "description": "Annotations added to Flowise worker pods" }, + "extraEnv": { "type": "array", "description": "Additional environment variables for Flowise workers", "items": { "type": "object" } }, + "resources": { "type": "object", "description": "CPU and memory requests/limits for Flowise workers" } + } + } + } + }, "redis": { "type": "object", "description": "Redis subchart or external Redis configuration" }, "storage": { "type": "object", diff --git a/charts/flowise/values.yaml b/charts/flowise/values.yaml index 15a6943dd..d71a07a8e 100644 --- a/charts/flowise/values.yaml +++ b/charts/flowise/values.yaml @@ -30,6 +30,8 @@ architecture: flowise: # -- Number of main Flowise server replicas replicaCount: 1 + # -- Delay before starting the main Flowise server, in seconds + startupDelaySeconds: 3 # -- Public application URL. Auto-detected from the first ingress host when empty. appUrl: "" # -- Log level: error | warn | info | verbose | debug @@ -138,6 +140,8 @@ queue: worker: # -- Number of Flowise worker replicas in queue mode replicaCount: 1 + # -- Delay before starting workers, in seconds. Keeps first-boot migrations owned by the main server. + startupDelaySeconds: 30 # -- Worker concurrency for BullMQ jobs concurrency: 100000 # -- Number of completed jobs kept by age