From 9c5766f8c5134d85221dd20301da27725db2e502 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Mon, 4 May 2026 12:10:15 +0200 Subject: [PATCH 01/10] Add support for expert broker and team broker api configs --- helm/flowfuse/templates/_helpers.tpl | 26 +++++++++++++++++++++++-- helm/flowfuse/templates/configmap.yaml | 8 ++++++++ helm/flowfuse/templates/deployment.yaml | 8 ++++++++ helm/flowfuse/templates/secrets.yaml | 3 +++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/helm/flowfuse/templates/_helpers.tpl b/helm/flowfuse/templates/_helpers.tpl index de41b3a4..d777b987 100644 --- a/helm/flowfuse/templates/_helpers.tpl +++ b/helm/flowfuse/templates/_helpers.tpl @@ -122,10 +122,11 @@ Note: The value for key .Values.postgresql.auth.existingSecret is inherited from */}} {{- define "forge.createSecret" -}} -{{- if not (and .Values.postgresql.auth.existingSecret +{{- if not (and .Values.postgresql.auth.existingSecret (not (and .Values.forge.email ((and .Values.forge.email.smtp (not .Values.forge.email.smtp.existingSecret))))) (not ((.Values.forge.assistant).enabled)) - (not ((.Values.forge.expert).enabled))) -}} + (not ((.Values.forge.expert).enabled)) + (not ((.Values.forge.broker.teamBroker).enabled))) -}} true {{- else -}} false @@ -346,4 +347,25 @@ Get the name from the release name. */}} {{- define "forge.name" -}} {{- .Release.Name -}} +{{- end -}} + +{{/* +Get the secret object name with Team Broker secret. +*/}} +{{- define "forge.teamBrokerSecretName" -}} +{{- if (.Values.forge.broker.teamBroker).enabled -}} + {{- printf "flowfuse-secrets" -}} +{{- end -}} +{{- end -}} + +{{/* +Create Team Broker API secret +*/}} +{{- define "forge.teamBrokerApiSecret" -}} +{{- if (.Values.forge.broker.teamBroker).enabled -}} +{{- $_ := required "A valid .Values.forge.broker.teamBroker.api.url is required!" ((.Values.forge.broker.teamBroker).api).url -}} +{{- $_ := required "A valid .Values.forge.broker.teamBroker.api.key is required!" ((.Values.forge.broker.teamBroker).api).key -}} +{{- $token := required "A valid .Values.forge.broker.teamBroker.api.secret is required!" ((.Values.forge.broker.teamBroker).api).secret -}} +teamBrokerApiSecret: {{ $token | b64enc | quote }} +{{- end -}} {{- end -}} \ No newline at end of file diff --git a/helm/flowfuse/templates/configmap.yaml b/helm/flowfuse/templates/configmap.yaml index a14907de..1205a878 100644 --- a/helm/flowfuse/templates/configmap.yaml +++ b/helm/flowfuse/templates/configmap.yaml @@ -224,6 +224,10 @@ data: teamBroker: enabled: true host: {{ include "forge.teamBrokerHost" . }} + api: + url: {{ .Values.forge.broker.teamBroker.api.url }} + key: {{ .Values.forge.broker.teamBroker.api.key }} + secret: <%= ENV['TEAM_BROKER_API_SECRET'] %> {{ end -}} {{- end }} logging: @@ -334,6 +338,10 @@ data: token: <%= ENV['EXPERT_TOKEN'] %> url: {{ ((.Values.forge.expert).service).url }} requestTimeout: {{ .Values.forge.expert.requestTimeout | default 60000 }} + {{- if ((.Values.forge.expert).broker).address }} + centralBroker: + server: {{ printf "%s:%v" .Values.forge.expert.broker.address .Values.forge.expert.broker.port }} + {{- end }} {{- end }} {{- end }} {{- if and (hasKey .Values.forge "npmRegistry") (hasKey .Values.forge.npmRegistry "enabled") }} diff --git a/helm/flowfuse/templates/deployment.yaml b/helm/flowfuse/templates/deployment.yaml index 2cbaf6df..27c121b7 100644 --- a/helm/flowfuse/templates/deployment.yaml +++ b/helm/flowfuse/templates/deployment.yaml @@ -90,6 +90,14 @@ spec: key: expertToken optional: true {{- end }} + {{- if (.Values.forge.broker.teamBroker).enabled }} + - name: TEAM_BROKER_API_SECRET + valueFrom: + secretKeyRef: + name: {{ include "forge.teamBrokerSecretName" . }} + key: teamBrokerApiSecret + optional: true + {{- end }} {{- if .Values.forge.localPostgresql }} - name: wait-for-local-db {{- $initWaitForLocalDbRegistry := (or .Values.forge.initContainers.waitForLocalDb.image.registry .Values.forge.registry) }} diff --git a/helm/flowfuse/templates/secrets.yaml b/helm/flowfuse/templates/secrets.yaml index 28cdc08e..e8626cfd 100644 --- a/helm/flowfuse/templates/secrets.yaml +++ b/helm/flowfuse/templates/secrets.yaml @@ -20,4 +20,7 @@ data: {{- if (include "forge.expertToken" . | trim) }} {{- include "forge.expertToken" . | nindent 2 -}} {{- end }} + {{- if (include "forge.teamBrokerApiSecret" . | trim) }} + {{- include "forge.teamBrokerApiSecret" . | nindent 2 -}} + {{- end }} {{- end }} \ No newline at end of file From b6fbfd15bacfc25e63359d5a914f67d3c0a856a4 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Mon, 4 May 2026 12:10:23 +0200 Subject: [PATCH 02/10] Add unit tests --- .../tests/expert_central_broker_test.yaml | 74 +++++++ helm/flowfuse/tests/team_broker_api_test.yaml | 200 ++++++++++++++++++ .../tests/team_broker_helpers_test.yaml | 137 ++++++++++++ 3 files changed, 411 insertions(+) create mode 100644 helm/flowfuse/tests/expert_central_broker_test.yaml create mode 100644 helm/flowfuse/tests/team_broker_api_test.yaml create mode 100644 helm/flowfuse/tests/team_broker_helpers_test.yaml diff --git a/helm/flowfuse/tests/expert_central_broker_test.yaml b/helm/flowfuse/tests/expert_central_broker_test.yaml new file mode 100644 index 00000000..ee8b079f --- /dev/null +++ b/helm/flowfuse/tests/expert_central_broker_test.yaml @@ -0,0 +1,74 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json +suite: test expert central broker configuration +templates: + - configmap.yaml +set: + forge.domain: "chart-unit-tests.com" +tests: + - it: should not include central broker block when expert is disabled + template: configmap.yaml + set: + forge.expert: + enabled: false + asserts: + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "centralBroker:" + + - it: should not include central broker block when expert key is absent + template: configmap.yaml + asserts: + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "centralBroker:" + + - it: should not include central broker block when expert is enabled but broker.address is not set + template: configmap.yaml + set: + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "expert:" + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "centralBroker:" + + - it: should render central broker server when expert is enabled with broker address and port + template: configmap.yaml + set: + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + broker: + address: "central-broker.example.com" + port: 1883 + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "centralBroker:" + - matchRegex: + path: data["flowforge.yml"] + pattern: "server: central-broker\\.example\\.com:1883" + + - it: should render central broker server with custom port + template: configmap.yaml + set: + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + broker: + address: "10.0.0.5" + port: 8883 + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "server: 10\\.0\\.0\\.5:8883" diff --git a/helm/flowfuse/tests/team_broker_api_test.yaml b/helm/flowfuse/tests/team_broker_api_test.yaml new file mode 100644 index 00000000..972b7bb3 --- /dev/null +++ b/helm/flowfuse/tests/team_broker_api_test.yaml @@ -0,0 +1,200 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json +suite: test team broker API configuration +templates: + - configmap.yaml + - deployment.yaml + - secrets.yaml +set: + forge.domain: "chart-unit-tests.com" +tests: + - it: should not include teamBroker api block when teamBroker is disabled by default + template: configmap.yaml + asserts: + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "teamBroker:" + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "TEAM_BROKER_API_SECRET" + + - it: should render teamBroker.api block with url, key and secret when enabled + template: configmap.yaml + set: + forge.broker.enabled: true + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "teamBroker:" + - matchRegex: + path: data["flowforge.yml"] + pattern: "url: https://team-broker\\.example\\.com" + - matchRegex: + path: data["flowforge.yml"] + pattern: "key: team-broker-key" + - matchRegex: + path: data["flowforge.yml"] + pattern: "secret: <%= ENV\\['TEAM_BROKER_API_SECRET'\\] %>" + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "secret: team-broker-secret" + + # Deployment tests + - it: should not include TEAM_BROKER_API_SECRET env var by default + template: deployment.yaml + asserts: + - notExists: + path: spec.template.spec.initContainers[0].env[?(@.name == "TEAM_BROKER_API_SECRET")] + + - it: should not include TEAM_BROKER_API_SECRET env var when teamBroker is disabled + template: deployment.yaml + set: + forge.broker.teamBroker: + enabled: false + asserts: + - notExists: + path: spec.template.spec.initContainers[0].env[?(@.name == "TEAM_BROKER_API_SECRET")] + + - it: should include TEAM_BROKER_API_SECRET env var when teamBroker is enabled + template: deployment.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - contains: + path: spec.template.spec.initContainers[0].env + content: + name: TEAM_BROKER_API_SECRET + valueFrom: + secretKeyRef: + name: flowfuse-secrets + key: teamBrokerApiSecret + optional: true + + # Secrets tests + - it: should not include teamBrokerApiSecret in secrets by default + template: secrets.yaml + asserts: + - notExists: + path: data.teamBrokerApiSecret + + - it: should not include teamBrokerApiSecret in secrets when teamBroker is disabled + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: false + asserts: + - notExists: + path: data.teamBrokerApiSecret + + - it: should include teamBrokerApiSecret base64 encoded when teamBroker is enabled + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - isKind: + of: Secret + - isNotNullOrEmpty: + path: data.teamBrokerApiSecret + - equal: + path: data.teamBrokerApiSecret + value: dGVhbS1icm9rZXItc2VjcmV0 # base64("team-broker-secret") + + - it: should fail when teamBroker is enabled but api.secret is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.secret is required!" + + - it: should fail when teamBroker is enabled but api block is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + + # Coexistence with other secrets + - it: should include teamBrokerApiSecret alongside other tokens when multiple features are enabled + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + forge.assistant: + enabled: true + service: + url: "https://assistant.example.com" + token: "assistant-token" + asserts: + - isKind: + of: Secret + - isNotNullOrEmpty: + path: data.teamBrokerApiSecret + - isNotNullOrEmpty: + path: data.expertToken + - isNotNullOrEmpty: + path: data.token + + - it: should include both EXPERT_TOKEN and TEAM_BROKER_API_SECRET env vars when both features are enabled + template: deployment.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + asserts: + - contains: + path: spec.template.spec.initContainers[0].env + content: + name: TEAM_BROKER_API_SECRET + valueFrom: + secretKeyRef: + name: flowfuse-secrets + key: teamBrokerApiSecret + optional: true + - contains: + path: spec.template.spec.initContainers[0].env + content: + name: EXPERT_TOKEN + valueFrom: + secretKeyRef: + name: flowfuse-secrets + key: expertToken + optional: true diff --git a/helm/flowfuse/tests/team_broker_helpers_test.yaml b/helm/flowfuse/tests/team_broker_helpers_test.yaml new file mode 100644 index 00000000..e0b9b243 --- /dev/null +++ b/helm/flowfuse/tests/team_broker_helpers_test.yaml @@ -0,0 +1,137 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json +suite: test team broker helper functions +templates: + - secrets.yaml +set: + forge.domain: "chart-unit-tests.com" +tests: + # forge.teamBrokerSecretName helper (verified indirectly through secret name on the rendered Secret) + - it: should render flowfuse-secrets when teamBroker is enabled + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - equal: + path: metadata.name + value: flowfuse-secrets + + - it: should not produce teamBrokerApiSecret data when teamBroker is disabled + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: false + asserts: + - notExists: + path: data.teamBrokerApiSecret + + # forge.teamBrokerApiSecret helper - encoding + - it: should base64 encode team broker api secret correctly + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "my-team-broker-secret-123" + asserts: + - equal: + path: data.teamBrokerApiSecret + value: bXktdGVhbS1icm9rZXItc2VjcmV0LTEyMw== # base64("my-team-broker-secret-123") + + # Integration with external secrets + - it: should work alongside external postgresql secret + template: secrets.yaml + set: + postgresql.auth.existingSecret: "external-db-secret" + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - isKind: + of: Secret + - isNotNullOrEmpty: + path: data.teamBrokerApiSecret + - notExists: + path: data.password + - notExists: + path: data.postgres-password + + - it: should not create the secret when teamBroker is disabled and external secrets are used + template: secrets.yaml + set: + postgresql.auth.existingSecret: "external-db-secret" + forge.email: + smtp: + host: smtp.example.com + existingSecret: "external-smtp-secret" + forge.broker.teamBroker: + enabled: false + asserts: + - hasDocuments: + count: 0 + + # Helper error handling: api.url, api.key and api.secret are all required together + - it: should require api.url when teamBroker is enabled and api.url is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + + - it: should require api.key when teamBroker is enabled and api.key is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + secret: "team-broker-secret" + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.key is required!" + + - it: should require api.secret when teamBroker is enabled and api.secret is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.secret is required!" + + - it: should require api fields when teamBroker is enabled but api is empty + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + api: {} + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + + - it: should require api fields when teamBroker is enabled but api is missing + template: secrets.yaml + set: + forge.broker.teamBroker: + enabled: true + # api is missing + asserts: + - failedTemplate: + errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" From b92db21d2c234b19486b345a3fe02ca535d0c3e8 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Mon, 4 May 2026 12:10:34 +0200 Subject: [PATCH 03/10] Update values schema --- helm/flowfuse/values.schema.json | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/helm/flowfuse/values.schema.json b/helm/flowfuse/values.schema.json index e5f2d5c4..5841984e 100644 --- a/helm/flowfuse/values.schema.json +++ b/helm/flowfuse/values.schema.json @@ -299,6 +299,21 @@ "properties": { "enabled": { "type": "boolean" + }, + "api": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "key": { + "type": "string" + }, + "secret": { + "type": "string" + } + } } } }, @@ -1021,6 +1036,19 @@ "type": "integer" } } + }, + "broker": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + } } } }, From 870bd3918a5603abd40f9a53deae46618739871d Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Mon, 4 May 2026 12:10:48 +0200 Subject: [PATCH 04/10] Update README --- helm/flowfuse/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/helm/flowfuse/README.md b/helm/flowfuse/README.md index ebc38a08..42ef652c 100644 --- a/helm/flowfuse/README.md +++ b/helm/flowfuse/README.md @@ -114,6 +114,9 @@ To use STMP to send email - `forge.broker.public_url` URL to access the broker from outside the cluster (default `ws://mqtt.[forge.domain]`, uses `wss://` if `forge.https` is `true`) - `forge.broker.hostname` the custom Fully Qualified Domain Name (FQDN) where the broker will be hosted (default `mqtt.[forge.domain]`) - `forge.broker.teamBroker.enabled` Enables Team Broker feature (default `false`) + - `forge.broker.teamBroker.api.url` URL for the Team Broker API (default not set) + - `forge.broker.teamBroker.api.key` API key for the Team Broker API (default not set) + - `forge.broker.teamBroker.api.secret` API secret for the Team Broker API (default not set) - `forge.broker.createMetricsUser` defines if a dedicated MQTT user with broker metrics collection permissions should be created (default `true`) - `forge.broker.affinity` allows to configure [affinity or anti-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) for the broker pod - `forge.broker.resources` allows to configure [resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the broker container @@ -304,7 +307,8 @@ Everything under `forge.rate_limits` is used as input to Fastify Rate Limit plug - `forge.expert.enabled` Enable/disable the FlowFuse Expert feature (default `false`) - `forge.expert.service.url` URL for the FlowFuse Expert service (default not set) - `forge.expert.service.token` Token for the FlowFuse Expert service (default not set) - - `forge.expert.service.requestTimeout` Timeout for the FlowFuse Expert service (default `60000`) + - `forge.expert.service.requestTimeout` Timeout for the FlowFuse Expert service (default `60000`) + - `forge.expert.broker.address` Address of the MQTT broker to use for communication with the Expert service (default not set) ### Ingress - `ingress.annotations` ingress annotations (default is `{}`). This value is also applied to Editor instances created by FlowFuse. From fdca048feeeb2352a0eb7170c3ec97e905b08b24 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 17:21:09 +0200 Subject: [PATCH 05/10] Expect team broker to be configured if expert broker is enabled --- helm/flowfuse/templates/configmap.yaml | 3 ++ .../tests/expert_central_broker_test.yaml | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/helm/flowfuse/templates/configmap.yaml b/helm/flowfuse/templates/configmap.yaml index 1205a878..4fc9c57e 100644 --- a/helm/flowfuse/templates/configmap.yaml +++ b/helm/flowfuse/templates/configmap.yaml @@ -339,6 +339,9 @@ data: url: {{ ((.Values.forge.expert).service).url }} requestTimeout: {{ .Values.forge.expert.requestTimeout | default 60000 }} {{- if ((.Values.forge.expert).broker).address }} + {{- if not ((.Values.forge.broker.teamBroker).enabled) }} + {{- fail "forge.expert.broker requires the Team Broker to be enabled (forge.broker.teamBroker.enabled=true)" -}} + {{- end }} centralBroker: server: {{ printf "%s:%v" .Values.forge.expert.broker.address .Values.forge.expert.broker.port }} {{- end }} diff --git a/helm/flowfuse/tests/expert_central_broker_test.yaml b/helm/flowfuse/tests/expert_central_broker_test.yaml index ee8b079f..26103097 100644 --- a/helm/flowfuse/tests/expert_central_broker_test.yaml +++ b/helm/flowfuse/tests/expert_central_broker_test.yaml @@ -41,6 +41,7 @@ tests: - it: should render central broker server when expert is enabled with broker address and port template: configmap.yaml set: + forge.broker.teamBroker.enabled: true forge.expert: enabled: true service: @@ -60,6 +61,7 @@ tests: - it: should render central broker server with custom port template: configmap.yaml set: + forge.broker.teamBroker.enabled: true forge.expert: enabled: true service: @@ -72,3 +74,34 @@ tests: - matchRegex: path: data["flowforge.yml"] pattern: "server: 10\\.0\\.0\\.5:8883" + + - it: should fail when expert broker address is set but team broker is not enabled + template: configmap.yaml + set: + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + broker: + address: "central-broker.example.com" + port: 1883 + asserts: + - failedTemplate: + errorMessage: "forge.expert.broker requires forge.broker.teamBroker.enabled=true (the expert central broker bridges through the team broker)" + + - it: should fail when expert broker address is set and team broker is explicitly disabled + template: configmap.yaml + set: + forge.broker.teamBroker.enabled: false + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + broker: + address: "central-broker.example.com" + port: 1883 + asserts: + - failedTemplate: + errorMessage: "forge.expert.broker requires forge.broker.teamBroker.enabled=true (the expert central broker bridges through the team broker)" From 1b05a2cec20c12eb43ae0c5c1748bdc0b1a42479 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 17:48:31 +0200 Subject: [PATCH 06/10] Add default expert broker port value --- helm/flowfuse/templates/configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/flowfuse/templates/configmap.yaml b/helm/flowfuse/templates/configmap.yaml index 4fc9c57e..11185564 100644 --- a/helm/flowfuse/templates/configmap.yaml +++ b/helm/flowfuse/templates/configmap.yaml @@ -343,7 +343,7 @@ data: {{- fail "forge.expert.broker requires the Team Broker to be enabled (forge.broker.teamBroker.enabled=true)" -}} {{- end }} centralBroker: - server: {{ printf "%s:%v" .Values.forge.expert.broker.address .Values.forge.expert.broker.port }} + server: {{ printf "%s:%v" .Values.forge.expert.broker.address (.Values.forge.expert.broker.port | default 8883) }} {{- end }} {{- end }} {{- end }} From c9843b41be07b7ffa54e030e2b9ed6b0aed613a7 Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 17:48:57 +0200 Subject: [PATCH 07/10] Adjust and fix expert broker tests --- .../tests/expert_central_broker_test.yaml | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/helm/flowfuse/tests/expert_central_broker_test.yaml b/helm/flowfuse/tests/expert_central_broker_test.yaml index 26103097..9606b374 100644 --- a/helm/flowfuse/tests/expert_central_broker_test.yaml +++ b/helm/flowfuse/tests/expert_central_broker_test.yaml @@ -69,11 +69,27 @@ tests: token: "expert-token" broker: address: "10.0.0.5" - port: 8883 + port: 1883 + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "server: 10\\.0\\.0\\.5:1883" + + - it: should default the central broker port to 8883 when not provided + template: configmap.yaml + set: + forge.broker.teamBroker.enabled: true + forge.expert: + enabled: true + service: + url: "https://expert.example.com" + token: "expert-token" + broker: + address: "central-broker.example.com" asserts: - matchRegex: path: data["flowforge.yml"] - pattern: "server: 10\\.0\\.0\\.5:8883" + pattern: "server: central-broker\\.example\\.com:8883" - it: should fail when expert broker address is set but team broker is not enabled template: configmap.yaml @@ -88,7 +104,7 @@ tests: port: 1883 asserts: - failedTemplate: - errorMessage: "forge.expert.broker requires forge.broker.teamBroker.enabled=true (the expert central broker bridges through the team broker)" + errorMessage: "forge.expert.broker requires the Team Broker to be enabled (forge.broker.teamBroker.enabled=true)" - it: should fail when expert broker address is set and team broker is explicitly disabled template: configmap.yaml @@ -104,4 +120,4 @@ tests: port: 1883 asserts: - failedTemplate: - errorMessage: "forge.expert.broker requires forge.broker.teamBroker.enabled=true (the expert central broker bridges through the team broker)" + errorMessage: "forge.expert.broker requires the Team Broker to be enabled (forge.broker.teamBroker.enabled=true)" From 951a5dca660bb8c55cfa83473b6f30d9f0bd945c Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 18:48:06 +0200 Subject: [PATCH 08/10] Use forge.teamBrokerApiUrl to define team broker api url --- helm/flowfuse/templates/_helpers.tpl | 12 +++++++++++- helm/flowfuse/templates/configmap.yaml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/helm/flowfuse/templates/_helpers.tpl b/helm/flowfuse/templates/_helpers.tpl index d777b987..fd38c278 100644 --- a/helm/flowfuse/templates/_helpers.tpl +++ b/helm/flowfuse/templates/_helpers.tpl @@ -358,12 +358,22 @@ Get the secret object name with Team Broker secret. {{- end -}} {{- end -}} +{{/* +Resolve Team Broker API URL: user-provided value, or default to the in-cluster EMQX dashboard service. +*/}} +{{- define "forge.teamBrokerApiUrl" -}} +{{- if ((.Values.forge.broker.teamBroker).api).url -}} + {{- .Values.forge.broker.teamBroker.api.url -}} +{{- else -}} + {{- printf "http://emqx-dashboard.%s:18083" .Release.Namespace -}} +{{- end -}} +{{- end -}} + {{/* Create Team Broker API secret */}} {{- define "forge.teamBrokerApiSecret" -}} {{- if (.Values.forge.broker.teamBroker).enabled -}} -{{- $_ := required "A valid .Values.forge.broker.teamBroker.api.url is required!" ((.Values.forge.broker.teamBroker).api).url -}} {{- $_ := required "A valid .Values.forge.broker.teamBroker.api.key is required!" ((.Values.forge.broker.teamBroker).api).key -}} {{- $token := required "A valid .Values.forge.broker.teamBroker.api.secret is required!" ((.Values.forge.broker.teamBroker).api).secret -}} teamBrokerApiSecret: {{ $token | b64enc | quote }} diff --git a/helm/flowfuse/templates/configmap.yaml b/helm/flowfuse/templates/configmap.yaml index 11185564..57a30578 100644 --- a/helm/flowfuse/templates/configmap.yaml +++ b/helm/flowfuse/templates/configmap.yaml @@ -225,7 +225,7 @@ data: enabled: true host: {{ include "forge.teamBrokerHost" . }} api: - url: {{ .Values.forge.broker.teamBroker.api.url }} + url: {{ include "forge.teamBrokerApiUrl" . }} key: {{ .Values.forge.broker.teamBroker.api.key }} secret: <%= ENV['TEAM_BROKER_API_SECRET'] %> {{ end -}} From 2006e56cd1ab6e28bc9f4babc898bbf452fc140a Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 18:48:14 +0200 Subject: [PATCH 09/10] Update readme --- helm/flowfuse/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/helm/flowfuse/README.md b/helm/flowfuse/README.md index 42ef652c..e9ef3df5 100644 --- a/helm/flowfuse/README.md +++ b/helm/flowfuse/README.md @@ -114,7 +114,7 @@ To use STMP to send email - `forge.broker.public_url` URL to access the broker from outside the cluster (default `ws://mqtt.[forge.domain]`, uses `wss://` if `forge.https` is `true`) - `forge.broker.hostname` the custom Fully Qualified Domain Name (FQDN) where the broker will be hosted (default `mqtt.[forge.domain]`) - `forge.broker.teamBroker.enabled` Enables Team Broker feature (default `false`) - - `forge.broker.teamBroker.api.url` URL for the Team Broker API (default not set) + - `forge.broker.teamBroker.api.url` URL for the Team Broker API (default `http://emqx-dashboard.:18083`) - `forge.broker.teamBroker.api.key` API key for the Team Broker API (default not set) - `forge.broker.teamBroker.api.secret` API secret for the Team Broker API (default not set) - `forge.broker.createMetricsUser` defines if a dedicated MQTT user with broker metrics collection permissions should be created (default `true`) @@ -308,7 +308,8 @@ Everything under `forge.rate_limits` is used as input to Fastify Rate Limit plug - `forge.expert.service.url` URL for the FlowFuse Expert service (default not set) - `forge.expert.service.token` Token for the FlowFuse Expert service (default not set) - `forge.expert.service.requestTimeout` Timeout for the FlowFuse Expert service (default `60000`) - - `forge.expert.broker.address` Address of the MQTT broker to use for communication with the Expert service (default not set) + - `forge.expert.broker.address` Address of the MQTT broker to use for communication with the Expert service (default not set). Requires `forge.broker.teamBroker.enabled=true`, since the local team broker bridges to this central broker. + - `forge.expert.broker.port` Port of the MQTT broker to use for communication with the Expert service (default `8883`) ### Ingress - `ingress.annotations` ingress annotations (default is `{}`). This value is also applied to Editor instances created by FlowFuse. From 9aba2c0841ed008e34c2516c631a3d279cc7699e Mon Sep 17 00:00:00 2001 From: ppawlowski Date: Tue, 5 May 2026 18:48:35 +0200 Subject: [PATCH 10/10] Update unit tests --- helm/flowfuse/tests/team_broker_api_test.yaml | 2 +- .../tests/team_broker_helpers_test.yaml | 50 ++++++++++++++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/helm/flowfuse/tests/team_broker_api_test.yaml b/helm/flowfuse/tests/team_broker_api_test.yaml index 972b7bb3..005227f1 100644 --- a/helm/flowfuse/tests/team_broker_api_test.yaml +++ b/helm/flowfuse/tests/team_broker_api_test.yaml @@ -133,7 +133,7 @@ tests: enabled: true asserts: - failedTemplate: - errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + errorMessage: "A valid .Values.forge.broker.teamBroker.api.key is required!" # Coexistence with other secrets - it: should include teamBrokerApiSecret alongside other tokens when multiple features are enabled diff --git a/helm/flowfuse/tests/team_broker_helpers_test.yaml b/helm/flowfuse/tests/team_broker_helpers_test.yaml index e0b9b243..0ef033d4 100644 --- a/helm/flowfuse/tests/team_broker_helpers_test.yaml +++ b/helm/flowfuse/tests/team_broker_helpers_test.yaml @@ -2,6 +2,7 @@ suite: test team broker helper functions templates: - secrets.yaml + - configmap.yaml set: forge.domain: "chart-unit-tests.com" tests: @@ -79,8 +80,8 @@ tests: - hasDocuments: count: 0 - # Helper error handling: api.url, api.key and api.secret are all required together - - it: should require api.url when teamBroker is enabled and api.url is missing + # Helper error handling: api.key and api.secret are required together; api.url has a default + - it: should not require api.url when teamBroker is enabled and api.url is missing template: secrets.yaml set: forge.broker.teamBroker: @@ -89,8 +90,8 @@ tests: key: "team-broker-key" secret: "team-broker-secret" asserts: - - failedTemplate: - errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + - isNotNullOrEmpty: + path: data.teamBrokerApiSecret - it: should require api.key when teamBroker is enabled and api.key is missing template: secrets.yaml @@ -124,7 +125,7 @@ tests: api: {} asserts: - failedTemplate: - errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + errorMessage: "A valid .Values.forge.broker.teamBroker.api.key is required!" - it: should require api fields when teamBroker is enabled but api is missing template: secrets.yaml @@ -134,4 +135,41 @@ tests: # api is missing asserts: - failedTemplate: - errorMessage: "A valid .Values.forge.broker.teamBroker.api.url is required!" + errorMessage: "A valid .Values.forge.broker.teamBroker.api.key is required!" + + # forge.teamBrokerApiUrl helper - verified indirectly through the rendered configmap + - it: should default api.url to in-cluster EMQX dashboard when not provided + template: configmap.yaml + release: + namespace: my-namespace + set: + forge.broker.enabled: true + forge.broker.teamBroker: + enabled: true + api: + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "url: http://emqx-dashboard\\.my-namespace:18083" + + - it: should use user-provided api.url when set + template: configmap.yaml + release: + namespace: my-namespace + set: + forge.broker.enabled: true + forge.broker.teamBroker: + enabled: true + api: + url: "https://team-broker.example.com" + key: "team-broker-key" + secret: "team-broker-secret" + asserts: + - matchRegex: + path: data["flowforge.yml"] + pattern: "url: https://team-broker\\.example\\.com" + - notMatchRegex: + path: data["flowforge.yml"] + pattern: "url: http://emqx-dashboard"