@yarkivaev/scada 2.3.114 → 2.3.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +109 -0
  2. package/charts/scada/Chart.yaml +2 -2
  3. package/charts/scada/templates/_helpers.tpl +12 -4
  4. package/charts/scada/templates/clickhouse.yaml +3 -3
  5. package/charts/scada/templates/collector.yaml +2 -2
  6. package/charts/scada/templates/namespace.yaml +6 -0
  7. package/charts/scada/templates/postgres.yaml +3 -3
  8. package/charts/scada/templates/processes.yaml +88 -0
  9. package/charts/scada/templates/rabbitmq-config.yaml +91 -0
  10. package/charts/scada/templates/rabbitmq.yaml +54 -113
  11. package/charts/scada/templates/site-server.yaml +5 -3
  12. package/charts/scada/templates/supervisor-sink.yaml +4 -2
  13. package/charts/scada/values.schema.json +22 -0
  14. package/charts/scada/values.yaml +21 -0
  15. package/index.js +3 -0
  16. package/package.json +8 -3
  17. package/src/application/deploy/collectorProcesses.js +43 -0
  18. package/src/application/deploy/expandFlows.js +21 -10
  19. package/src/application/deploy/publishedStreams.js +30 -0
  20. package/src/application/deploy/releaseEdges.js +13 -16
  21. package/src/application/deploy/releaseNodes.js +13 -0
  22. package/src/application/deploy/releaseValues.js +4 -15
  23. package/src/application/deploy/releases.js +3 -1
  24. package/src/application/deploy/subscriptions.js +20 -0
  25. package/src/infrastructure/deploy/executeRelease.js +102 -0
  26. package/src/infrastructure/ingest/durableConsume.js +2 -169
  27. package/src/infrastructure/ingest/modbus/silentStreams.js +9 -11
  28. package/src/infrastructure/ingest/silentOpenWatch.js +5 -42
  29. package/src/infrastructure/ingest/telemetry/amqpMetricsIngest.js +7 -18
  30. package/src/infrastructure/ingest/telemetry/amqpMqttRelay.js +8 -17
  31. package/src/infrastructure/messaging/rabbitmq/definitions.js +24 -0
  32. package/src/infrastructure/messaging/rabbitmq/reconcile.js +104 -0
  33. package/src/infrastructure/opcua/config.js +38 -0
  34. package/src/infrastructure/opcua/endpoint.js +80 -0
  35. package/src/infrastructure/opcua/index.js +10 -0
  36. package/src/infrastructure/opcua/session.js +60 -0
  37. package/src/infrastructure/opcua/source.js +35 -0
  38. package/src/infrastructure/opcua/subscriptionHealth.js +28 -0
  39. package/src/infrastructure/operators/operatorsSync.js +5 -29
  40. package/src/infrastructure/persistence/clickhouse/streamHub.js +21 -15
  41. package/src/infrastructure/persistence/metricsSensor.js +9 -6
  42. package/src/infrastructure/sync/operationSyncIngest.js +4 -16
package/README.md CHANGED
@@ -269,6 +269,30 @@ Opaque operation `kind` strings and generic ports (`latestForMachine`, list/upse
269
269
  | Export ports `Query` / `Stream` / `Sink` / `Job` | Export jobs + destination adapters |
270
270
  | ClickHouse + PG adapters | Plant wiring (`sokolPlant` / `edgePlant`) |
271
271
 
272
+ ### Stream transport ownership
273
+
274
+ `@yarkivaev/source-to-sink` owns reusable Node.js sources, sinks and transport
275
+ lifecycle: serial input, Modbus polling,
276
+ MQTT/STOMP messaging, AMQP consumption and confirmed publishing, and serialized
277
+ periodic ticks. SCADA composes these primitives with generic codecs, domain ports
278
+ and persistence adapters. Plant packages supply source mappings, payload formats,
279
+ queries, routing and plant-specific behavior.
280
+
281
+ The SCADA AMQP telemetry, MQTT relay and operation-sync pipelines use
282
+ `amqpSource`. It acknowledges after collector acceptance, requeues failures by
283
+ default, and avoids settling explicitly acknowledged or rejected deliveries twice.
284
+ Shutdown cancels consumption, drains accepted work and settlements, then closes
285
+ the channel and connection. Reconnect and restart wait for that drain.
286
+ `durableConsume` remains a compatibility facade over the shared AMQP session.
287
+ Collectors that batch records acknowledge acceptance into that collector; this is
288
+ not a guarantee that a later batch has already reached persistent storage.
289
+
290
+ `intervalSource` schedules operator-cache synchronization, stale-open-segment
291
+ sweeps, silent-stream reconciliation, PostgreSQL metric streams and the shared
292
+ ClickHouse stream hub. Polls do not overlap; metric streams await subscribers
293
+ before advancing their cursors. HTTP keepalive timers and domain coalescing
294
+ windows remain owned by their respective adapters and domain policies.
295
+
272
296
  Depend on a pinned version (`"@yarkivaev/scada": "…#v2.3.46"`), not a local path, in downstream packages.
273
297
 
274
298
  ## CI/CD
@@ -286,3 +310,88 @@ The scada chart declares the queue and applies an ingest-only dead-letter policy
286
310
  without changing existing queue arguments. The profile `env` is passed to both
287
311
  site-server and supervisor-sink. DLQ records retain the original body and RabbitMQ
288
312
  `x-death` metadata for inspection and deliberate replay; they are not auto-expired.
313
+
314
+ ### Deploy a node with named processes
315
+
316
+ A profile can expose deployment coordinates in `target`, set
317
+ `apps.scada.site.enabled` and `apps.scada.sink.enabled` independently, and
318
+ provide RabbitMQ chart settings in `modules.message_bus.chart`. The
319
+ `releases(document, cluster)` projection returns the target and chart values.
320
+ The default profile still renders the existing site and sink.
321
+
322
+ `subscriptions(expandedDocument, sourceId)` returns the outgoing server
323
+ subscriptions as `{ from, to, name, api? }`. It combines sender `fan_out` and
324
+ receiver `fan_in` declarations, keeping each expanded stream path and any
325
+ explicit service selected by the receiver. Declaring the same subscription on
326
+ both sides produces a single row.
327
+
328
+ Use `collectorProcesses(machines, describe, project)` to derive workloads from
329
+ the selected server's source rows. For each row, `describe(row)` returns a
330
+ process name, command and optional environment and resources. Rows with the
331
+ same name must describe the same process. `project(descriptor, rows)` converts
332
+ its sources into adapter-specific environment variables and host devices. Process
333
+ order follows the first occurrence of each name in the source rows.
334
+ The packaged `charts/scada` chart renders the resulting `processes` as
335
+ named Deployments. A configured broker claim name selects a standalone PVC.
336
+ The default storage mode uses StatefulSet claim templates.
337
+
338
+ `executeRelease(plan, action)` renders the chart or runs check, diff, adopt
339
+ and apply against the explicitly supplied Kubernetes context and namespace.
340
+ During `adopt`, the legacy client-side manager migrates the resources first.
341
+ The release's server-side manager then applies the same rendered manifest before
342
+ the executor waits for the workloads. This restores declared fields that a
343
+ legacy three-way merge can drop, such as an environment entry whose name was
344
+ duplicated in the old last-applied annotation. Each apply gets its own server
345
+ dry-run. `check-adoption` previews the client-side phase only. The server-side
346
+ phase must run against the migrated state. An ownership conflict stops adoption
347
+ before rollout waits and the afterReady hook. Resolve the field ownership and
348
+ rerun `apply` after the client-side migration has completed.
349
+ A caller can supply prerequisites, rollout resources and an afterReady hook.
350
+ The RabbitMQ definitions builder and management reconciler are exported as
351
+ `@yarkivaev/scada/rabbitmq/definitions` and `@yarkivaev/scada/rabbitmq/reconcile`.
352
+ The caller supplies the vhost, connection settings and ownership policy for
353
+ retired routes. Reconciliation retains stored queues.
354
+
355
+ ### Collect multiple OPC UA endpoints
356
+
357
+ `@yarkivaev/scada/opcua` exports `opcuaConnections(connections, publish, options)`
358
+ and `validateConnections(connections)`. The same entry point exports
359
+ `opcuaSource(connections, collector, options)` for source-to-sink composition;
360
+ the collector accepts `{ channel, value }`. SCADA owns the OPC UA runtime and
361
+ its `node-opcua` dependency; source-to-sink contains no OPC UA implementation.
362
+ Offline deployment planners should import
363
+ `validateConnections` from `@yarkivaev/scada/opcua/config`; this entry point never
364
+ loads the network library or its startup diagnostics. Each connection has a unique `url`,
365
+ positive `publishMs` and `heartbeatMs`, and `channels` with explicit, unique
366
+ `nodeId` values. Additional channel metadata is opaque to this library.
367
+
368
+ ```js
369
+ import opcuaConnections from '@yarkivaev/scada/opcua';
370
+
371
+ const collector = opcuaConnections([
372
+ { url: 'opc.tcp://192.0.2.10:4840', publishMs: 100, heartbeatMs: 1000,
373
+ channels: [{ nodeId: 'ns=1;s=temperature', topic: 'line/temperature' }] }
374
+ ], (channel, value) => publish(channel.topic, value));
375
+ await collector.start();
376
+ // On process shutdown:
377
+ await collector.stop();
378
+ ```
379
+
380
+ Each endpoint owns its client, session and subscription. An unavailable endpoint
381
+ retries independently while others continue. Start schedules these workers; it
382
+ does not wait for every server to be available. Samples are delivered only with
383
+ Good quality, both on change and through serialized fresh heartbeat reads.
384
+ The caller owns value conversion, routing, timestamps and destination transport.
385
+ Options `retryMs`, `connectTimeoutMs` and `requestTimeoutMs` bound retry timing and
386
+ network requests; `onError(error, connection)` receives connection diagnostics.
387
+ Do not replicate a collector with the same sources to distribute load: assign
388
+ disjoint source sets to separate instances instead.
389
+
390
+ A release can replace old collectors using
391
+ `retirements: [{ workload: 'deployment/old-reader', selector: 'app=old-reader' }]`.
392
+ For apply/adopt, the executor validates the new manifests first, foreground-deletes
393
+ only these explicit Deployments, and verifies that no selected old pods remain
394
+ before applying the replacement. This trades a collection gap for no overlap.
395
+ The caller must bind each selector to its old Deployment; no namespace-wide prune
396
+ is performed. Render/check/diff do not delete anything. An optional process `image`
397
+ overrides the release image, so unrelated collectors can retain their pinned runtime.
@@ -2,5 +2,5 @@ apiVersion: v2
2
2
  name: scada
3
3
  description: Generic SCADA site — site-server, supervisor-sink, collectors, Postgres, RabbitMQ, optional ClickHouse
4
4
  type: application
5
- version: 0.2.4
6
- appVersion: "2.3.114"
5
+ version: 0.3.0
6
+ appVersion: "2.3.115"
@@ -1,3 +1,7 @@
1
+ {{- define "scada.prefix" -}}
2
+ {{- default .Release.Name .Values.prefix -}}
3
+ {{- end }}
4
+
1
5
  {{- define "scada.labels" -}}
2
6
  app.kubernetes.io/name: scada
3
7
  app.kubernetes.io/instance: {{ .Release.Name }}
@@ -69,7 +73,7 @@ volumeMode: Filesystem
69
73
  {{- end }}
70
74
  {{- if .Values.rabbitmq.enabled }}
71
75
  - name: STOMP_URL
72
- value: {{ printf "stomp://%s-rabbitmq:61613" .Release.Name | quote }}
76
+ value: {{ printf "stomp://%s-rabbitmq:61613" (include "scada.prefix" .) | quote }}
73
77
  - name: STOMP_LOGIN
74
78
  value: guest
75
79
  - name: STOMP_PASSCODE
@@ -77,12 +81,16 @@ volumeMode: Filesystem
77
81
  - name: STOMP_HOST
78
82
  value: /
79
83
  - name: AMQP_URL
80
- value: {{ printf "amqp://guest:guest@%s-rabbitmq:5672" .Release.Name | quote }}
84
+ value: {{ printf "amqp://guest:guest@%s-rabbitmq:5672" (include "scada.prefix" .) | quote }}
81
85
  {{- end }}
82
86
  {{- if .Values.clickhouse.enabled }}
83
87
  - name: CLICKHOUSE_HOST
84
- value: {{ printf "%s-clickhouse" .Release.Name | quote }}
88
+ value: {{ printf "%s-clickhouse" (include "scada.prefix" .) | quote }}
85
89
  - name: CLICKHOUSE_URL
86
- value: {{ printf "http://%s-clickhouse:8123" .Release.Name | quote }}
90
+ value: {{ printf "http://%s-clickhouse:8123" (include "scada.prefix" .) | quote }}
87
91
  {{- end }}
88
92
  {{- end }}
93
+
94
+ {{- define "scada.brokerConfigName" -}}
95
+ {{- default (printf "%s-rabbitmq" (include "scada.prefix" .)) .Values.rabbitmq.configName -}}
96
+ {{- end }}
@@ -2,7 +2,7 @@
2
2
  apiVersion: v1
3
3
  kind: Service
4
4
  metadata:
5
- name: {{ .Release.Name }}-clickhouse
5
+ name: {{ include "scada.prefix" . }}-clickhouse
6
6
  namespace: {{ .Release.Namespace }}
7
7
  labels:
8
8
  {{- include "scada.labels" . | nindent 4 }}
@@ -24,13 +24,13 @@ spec:
24
24
  apiVersion: apps/v1
25
25
  kind: StatefulSet
26
26
  metadata:
27
- name: {{ .Release.Name }}-clickhouse
27
+ name: {{ include "scada.prefix" . }}-clickhouse
28
28
  namespace: {{ .Release.Namespace }}
29
29
  labels:
30
30
  {{- include "scada.labels" . | nindent 4 }}
31
31
  app: clickhouse
32
32
  spec:
33
- serviceName: {{ .Release.Name }}-clickhouse
33
+ serviceName: {{ include "scada.prefix" . }}-clickhouse
34
34
  replicas: 1
35
35
  selector:
36
36
  matchLabels:
@@ -53,7 +53,7 @@
53
53
  apiVersion: apps/v1
54
54
  kind: Deployment
55
55
  metadata:
56
- name: {{ $.Release.Name }}-collector-{{ $slug }}
56
+ name: {{ include "scada.prefix" $ }}-collector-{{ $slug }}
57
57
  namespace: {{ $.Release.Namespace }}
58
58
  labels:
59
59
  {{- include "scada.labels" $ | nindent 4 }}
@@ -97,7 +97,7 @@ spec:
97
97
  value: {{ $maps | toJson | quote }}
98
98
  {{- if $.Values.rabbitmq.enabled }}
99
99
  - name: MQTT_URL
100
- value: {{ printf "mqtt://%s-rabbitmq:1883" $.Release.Name | quote }}
100
+ value: {{ printf "mqtt://%s-rabbitmq:1883" (include "scada.prefix" $) | quote }}
101
101
  {{- end }}
102
102
  resources:
103
103
  {{- toYaml $.Values.resources | nindent 12 }}
@@ -0,0 +1,6 @@
1
+ {{- if .Values.createNamespace }}
2
+ apiVersion: v1
3
+ kind: Namespace
4
+ metadata:
5
+ name: {{ .Release.Namespace }}
6
+ {{- end }}
@@ -2,7 +2,7 @@
2
2
  apiVersion: v1
3
3
  kind: Service
4
4
  metadata:
5
- name: {{ .Release.Name }}-postgres
5
+ name: {{ include "scada.prefix" . }}-postgres
6
6
  namespace: {{ .Release.Namespace }}
7
7
  labels:
8
8
  {{- include "scada.labels" . | nindent 4 }}
@@ -18,13 +18,13 @@ spec:
18
18
  apiVersion: apps/v1
19
19
  kind: StatefulSet
20
20
  metadata:
21
- name: {{ .Release.Name }}-postgres
21
+ name: {{ include "scada.prefix" . }}-postgres
22
22
  namespace: {{ .Release.Namespace }}
23
23
  labels:
24
24
  {{- include "scada.labels" . | nindent 4 }}
25
25
  app: postgres
26
26
  spec:
27
- serviceName: {{ .Release.Name }}-postgres
27
+ serviceName: {{ include "scada.prefix" . }}-postgres
28
28
  replicas: 1
29
29
  selector:
30
30
  matchLabels:
@@ -0,0 +1,88 @@
1
+ {{- $names := dict }}
2
+ {{- $devices := dict }}
3
+ {{- range .Values.processes }}
4
+ {{- $name := required "process name is required" .name }}
5
+ {{- if hasKey $names $name }}
6
+ {{- fail (printf "duplicate process name %s" $name) }}
7
+ {{- end }}
8
+ {{- $_ := set $names $name true }}
9
+ {{- range .hostDevices }}
10
+ {{- if hasKey $devices . }}
11
+ {{- fail (printf "processes %s and %s both claim %s" $name (get $devices .) .) }}
12
+ {{- end }}
13
+ {{- $_ := set $devices . $name }}
14
+ {{- end }}
15
+ ---
16
+ apiVersion: apps/v1
17
+ kind: Deployment
18
+ metadata:
19
+ name: {{ include "scada.prefix" $ }}-{{ $name }}
20
+ namespace: {{ $.Release.Namespace }}
21
+ labels:
22
+ {{- include "scada.labels" $ | nindent 4 }}
23
+ app: {{ $name }}
24
+ {{- with $.Values.workloadLabels }}
25
+ {{- toYaml . | nindent 4 }}
26
+ {{- end }}
27
+ spec:
28
+ replicas: 1
29
+ strategy:
30
+ type: Recreate
31
+ rollingUpdate: null
32
+ selector:
33
+ matchLabels:
34
+ {{- include "scada.match" (dict "app" $name "ctx" $) | nindent 6 }}
35
+ template:
36
+ metadata:
37
+ labels:
38
+ {{- include "scada.labels" $ | nindent 8 }}
39
+ app: {{ $name }}
40
+ {{- with $.Values.workloadLabels }}
41
+ {{- toYaml . | nindent 8 }}
42
+ {{- end }}
43
+ {{- if and $.Values.rabbitmq.enabled $.Values.rabbitmq.rolloutOnConfigChange }}
44
+ annotations:
45
+ checksum/broker: {{ include (print $.Template.BasePath "/rabbitmq-config.yaml") $ | sha256sum }}
46
+ {{- end }}
47
+ spec:
48
+ {{- with $.Values.pullSecret }}
49
+ imagePullSecrets:
50
+ - name: {{ . }}
51
+ {{- end }}
52
+ {{- with .podSecurityContext }}
53
+ securityContext:
54
+ {{- toYaml . | nindent 8 }}
55
+ {{- end }}
56
+ containers:
57
+ - name: {{ $name }}
58
+ image: {{ required "process image is required" (default $.Values.image .image) | quote }}
59
+ imagePullPolicy: IfNotPresent
60
+ command: {{ required "process command is required" .command | toJson }}
61
+ {{- if .hostDevices }}
62
+ securityContext:
63
+ privileged: true
64
+ volumeMounts:
65
+ {{- range $index, $path := .hostDevices }}
66
+ - name: serial-{{ add1 $index }}
67
+ mountPath: {{ $path | quote }}
68
+ {{- end }}
69
+ {{- end }}
70
+ {{- with .env }}
71
+ env:
72
+ {{- range $key, $value := . }}
73
+ - name: {{ $key }}
74
+ value: {{ $value | quote }}
75
+ {{- end }}
76
+ {{- end }}
77
+ resources:
78
+ {{- toYaml (default $.Values.resources .resources) | nindent 12 }}
79
+ {{- with .hostDevices }}
80
+ volumes:
81
+ {{- range $index, $path := . }}
82
+ - name: serial-{{ add1 $index }}
83
+ hostPath:
84
+ path: {{ $path | quote }}
85
+ type: CharDevice
86
+ {{- end }}
87
+ {{- end }}
88
+ {{- end }}
@@ -0,0 +1,91 @@
1
+ {{- if .Values.rabbitmq.enabled }}
2
+ {{- $ingest := list -}}
3
+ {{- $outgoing := list -}}
4
+ {{- range .Values.collectors }}
5
+ {{- if .path }}
6
+ {{- $ingest = append $ingest (include "scada.routingKey" .path | trim) -}}
7
+ {{- end }}
8
+ {{- end }}
9
+ {{- range $item := concat (.Values.buses | default (list)) (.Values.bus | default (list)) }}
10
+ {{- range $name := ($item.names | default (list)) }}
11
+ {{- $routed := include "scada.routingKey" $name | trim -}}
12
+ {{- $ingest = append $ingest $routed -}}
13
+ {{- $outgoing = append $outgoing $routed -}}
14
+ {{- end }}
15
+ {{- end }}
16
+ {{- $outgoing = $outgoing | uniq -}}
17
+ {{- if $ingest }}
18
+ {{- $ingest = $ingest | uniq -}}
19
+ {{- else }}
20
+ {{- $ingest = list "#" -}}
21
+ {{- end }}
22
+ {{- $exchange := list
23
+ (dict "name" "scada.alerts" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
24
+ (dict "name" "scada.segments" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
25
+ (dict "name" "scada.telemetry" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
26
+ (dict "name" "scada.operations" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
27
+ (dict "name" "scada.user_decisions" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
28
+ -}}
29
+ {{- $queue := list
30
+ (dict "name" "scada.segments.ingest" "vhost" "/" "durable" true "auto_delete" false)
31
+ (dict "name" "scada.telemetry.ingest" "vhost" "/" "durable" true "auto_delete" false)
32
+ (dict "name" "scada.operations.ingest" "vhost" "/" "durable" true "auto_delete" false)
33
+ (dict "name" "scada.user_decisions" "vhost" "/" "durable" true "auto_delete" false)
34
+ -}}
35
+ {{- if $outgoing }}
36
+ {{- $queue = append $queue (dict "name" "scada.telemetry.outbox" "vhost" "/" "durable" true "auto_delete" false) -}}
37
+ {{- end }}
38
+ {{- $policies := list -}}
39
+ {{- with .Values.rabbitmq.deadLetter }}
40
+ {{- $queue = append $queue (dict "name" .queue "vhost" "/" "durable" true "auto_delete" false) -}}
41
+ {{- $policies = append $policies (dict "name" "ingest-dlq" "vhost" "/" "pattern" "^scada\\.(segments|operations|telemetry)\\.ingest$" "apply-to" "queues" "priority" 10 "definition" (dict "dead-letter-exchange" "" "dead-letter-routing-key" .queue)) -}}
42
+ {{- end }}
43
+ {{- $binding := list -}}
44
+ {{- range $key := $ingest }}
45
+ {{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.ingest" "destination_type" "queue" "routing_key" $key) -}}
46
+ {{- end }}
47
+ {{- range $pair := (list
48
+ (dict "source" "scada.operations" "destination" "scada.operations.ingest")
49
+ (dict "source" "scada.segments" "destination" "scada.segments.ingest")
50
+ (dict "source" "scada.user_decisions" "destination" "scada.user_decisions")) }}
51
+ {{- $binding = append $binding (dict "source" $pair.source "vhost" "/" "destination" $pair.destination "destination_type" "queue" "routing_key" "#") -}}
52
+ {{- end }}
53
+ {{- range $key := $outgoing }}
54
+ {{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.outbox" "destination_type" "queue" "routing_key" $key) -}}
55
+ {{- end }}
56
+ {{- $definitions := dict
57
+ "vhosts" (list (dict "name" "/"))
58
+ "users" (list (dict "name" "guest" "password" "guest" "tags" (list "administrator")))
59
+ "permissions" (list (dict "user" "guest" "vhost" "/" "configure" ".*" "write" ".*" "read" ".*"))
60
+ "exchanges" $exchange
61
+ "queues" $queue
62
+ "policies" $policies
63
+ "bindings" $binding
64
+ -}}
65
+ apiVersion: v1
66
+ kind: ConfigMap
67
+ metadata:
68
+ name: {{ include "scada.brokerConfigName" . }}
69
+ namespace: {{ .Release.Namespace }}
70
+ labels:
71
+ {{- include "scada.labels" . | nindent 4 }}
72
+ app: rabbitmq
73
+ data:
74
+ enabled_plugins: |
75
+ [{{ join ", " .Values.rabbitmq.plugins }}].
76
+ rabbitmq.conf: |
77
+ load_definitions = /etc/rabbitmq/definitions.json
78
+ mqtt.listeners.tcp.default = 1883
79
+ mqtt.allow_anonymous = true
80
+ mqtt.vhost = /
81
+ mqtt.exchange = scada.telemetry
82
+ {{- range $key, $value := .Values.rabbitmq.extraConfig }}
83
+ {{ $key }} = {{ $value }}
84
+ {{- end }}
85
+ definitions.json: |
86
+ {{- if ne .Values.rabbitmq.definitions nil }}
87
+ {{ .Values.rabbitmq.definitions | toPrettyJson | nindent 4 }}
88
+ {{- else }}
89
+ {{ $definitions | toPrettyJson | nindent 4 }}
90
+ {{- end }}
91
+ {{- end }}
@@ -1,91 +1,8 @@
1
1
  {{- if .Values.rabbitmq.enabled }}
2
- {{- $ingest := list -}}
3
- {{- $outgoing := list -}}
4
- {{- range .Values.collectors }}
5
- {{- if .path }}
6
- {{- $ingest = append $ingest (include "scada.routingKey" .path | trim) -}}
7
- {{- end }}
8
- {{- end }}
9
- {{- range $item := concat (.Values.buses | default (list)) (.Values.bus | default (list)) }}
10
- {{- range $name := ($item.names | default (list)) }}
11
- {{- $routed := include "scada.routingKey" $name | trim -}}
12
- {{- $ingest = append $ingest $routed -}}
13
- {{- $outgoing = append $outgoing $routed -}}
14
- {{- end }}
15
- {{- end }}
16
- {{- $outgoing = $outgoing | uniq -}}
17
- {{- if $ingest }}
18
- {{- $ingest = $ingest | uniq -}}
19
- {{- else }}
20
- {{- $ingest = list "#" -}}
21
- {{- end }}
22
- {{- $exchange := list
23
- (dict "name" "scada.alerts" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
24
- (dict "name" "scada.segments" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
25
- (dict "name" "scada.telemetry" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
26
- (dict "name" "scada.operations" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
27
- (dict "name" "scada.user_decisions" "vhost" "/" "type" "topic" "durable" true "auto_delete" false)
28
- -}}
29
- {{- $queue := list
30
- (dict "name" "scada.segments.ingest" "vhost" "/" "durable" true "auto_delete" false)
31
- (dict "name" "scada.telemetry.ingest" "vhost" "/" "durable" true "auto_delete" false)
32
- (dict "name" "scada.operations.ingest" "vhost" "/" "durable" true "auto_delete" false)
33
- (dict "name" "scada.user_decisions" "vhost" "/" "durable" true "auto_delete" false)
34
- -}}
35
- {{- if $outgoing }}
36
- {{- $queue = append $queue (dict "name" "scada.telemetry.outbox" "vhost" "/" "durable" true "auto_delete" false) -}}
37
- {{- end }}
38
- {{- $policies := list -}}
39
- {{- with .Values.rabbitmq.deadLetter }}
40
- {{- $queue = append $queue (dict "name" .queue "vhost" "/" "durable" true "auto_delete" false) -}}
41
- {{- $policies = append $policies (dict "name" "ingest-dlq" "vhost" "/" "pattern" "^scada\\.(segments|operations|telemetry)\\.ingest$" "apply-to" "queues" "priority" 10 "definition" (dict "dead-letter-exchange" "" "dead-letter-routing-key" .queue)) -}}
42
- {{- end }}
43
- {{- $binding := list -}}
44
- {{- range $key := $ingest }}
45
- {{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.ingest" "destination_type" "queue" "routing_key" $key) -}}
46
- {{- end }}
47
- {{- range $pair := (list
48
- (dict "source" "scada.operations" "destination" "scada.operations.ingest")
49
- (dict "source" "scada.segments" "destination" "scada.segments.ingest")
50
- (dict "source" "scada.user_decisions" "destination" "scada.user_decisions")) }}
51
- {{- $binding = append $binding (dict "source" $pair.source "vhost" "/" "destination" $pair.destination "destination_type" "queue" "routing_key" "#") -}}
52
- {{- end }}
53
- {{- range $key := $outgoing }}
54
- {{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.outbox" "destination_type" "queue" "routing_key" $key) -}}
55
- {{- end }}
56
- {{- $definitions := dict
57
- "vhosts" (list (dict "name" "/"))
58
- "users" (list (dict "name" "guest" "password" "guest" "tags" (list "administrator")))
59
- "permissions" (list (dict "user" "guest" "vhost" "/" "configure" ".*" "write" ".*" "read" ".*"))
60
- "exchanges" $exchange
61
- "queues" $queue
62
- "policies" $policies
63
- "bindings" $binding
64
- -}}
65
- apiVersion: v1
66
- kind: ConfigMap
67
- metadata:
68
- name: {{ .Release.Name }}-rabbitmq
69
- namespace: {{ .Release.Namespace }}
70
- labels:
71
- {{- include "scada.labels" . | nindent 4 }}
72
- app: rabbitmq
73
- data:
74
- enabled_plugins: |
75
- [rabbitmq_stomp, rabbitmq_mqtt, rabbitmq_management].
76
- rabbitmq.conf: |
77
- load_definitions = /etc/rabbitmq/definitions.json
78
- mqtt.listeners.tcp.default = 1883
79
- mqtt.allow_anonymous = true
80
- mqtt.vhost = /
81
- mqtt.exchange = scada.telemetry
82
- definitions.json: |
83
- {{ $definitions | toPrettyJson | nindent 4 }}
84
- ---
85
2
  apiVersion: v1
86
3
  kind: Service
87
4
  metadata:
88
- name: {{ .Release.Name }}-rabbitmq
5
+ name: {{ include "scada.prefix" . }}-rabbitmq
89
6
  namespace: {{ .Release.Namespace }}
90
7
  labels:
91
8
  {{- include "scada.labels" . | nindent 4 }}
@@ -94,46 +11,30 @@ spec:
94
11
  {{- $svc := .Values.rabbitmq.service | default dict }}
95
12
  type: {{ $svc.type | default "ClusterIP" }}
96
13
  ports:
97
- - name: amqp
98
- port: 5672
99
- targetPort: amqp
100
- {{- with $svc.nodePorts }}
101
- {{- if .amqp }}
102
- nodePort: {{ .amqp }}
103
- {{- end }}
104
- {{- end }}
105
- - name: mqtt
106
- port: 1883
107
- targetPort: mqtt
108
- - name: stomp
109
- port: 61613
110
- targetPort: stomp
111
- {{- with $svc.nodePorts }}
112
- {{- if .stomp }}
113
- nodePort: {{ .stomp }}
114
- {{- end }}
115
- {{- end }}
116
- - name: management
117
- port: 15672
118
- targetPort: management
119
- {{- with $svc.nodePorts }}
120
- {{- if .management }}
121
- nodePort: {{ .management }}
122
- {{- end }}
14
+ {{- range $entry := list (dict "name" "amqp" "port" 5672) (dict "name" "mqtt" "port" 1883) (dict "name" "stomp" "port" 61613) (dict "name" "management" "port" 15672) }}
15
+ - name: {{ $entry.name }}
16
+ port: {{ $entry.port }}
17
+ targetPort: {{ ternary $entry.port $entry.name ($svc.numericTargetPorts | default false) }}
18
+ {{- with get ($svc.nodePorts | default dict) $entry.name }}
19
+ nodePort: {{ . }}
123
20
  {{- end }}
21
+ {{- end }}
124
22
  selector:
125
23
  {{- include "scada.match" (dict "app" "rabbitmq" "ctx" .) | nindent 4 }}
126
24
  ---
127
25
  apiVersion: apps/v1
128
26
  kind: StatefulSet
129
27
  metadata:
130
- name: {{ .Release.Name }}-rabbitmq
28
+ name: {{ include "scada.prefix" . }}-rabbitmq
131
29
  namespace: {{ .Release.Namespace }}
132
30
  labels:
133
31
  {{- include "scada.labels" . | nindent 4 }}
134
32
  app: rabbitmq
33
+ {{- with .Values.workloadLabels }}
34
+ {{- toYaml . | nindent 4 }}
35
+ {{- end }}
135
36
  spec:
136
- serviceName: {{ .Release.Name }}-rabbitmq
37
+ serviceName: {{ include "scada.prefix" . }}-rabbitmq
137
38
  replicas: 1
138
39
  selector:
139
40
  matchLabels:
@@ -143,10 +44,24 @@ spec:
143
44
  labels:
144
45
  {{- include "scada.labels" . | nindent 8 }}
145
46
  {{- include "scada.match" (dict "app" "rabbitmq" "ctx" .) | nindent 8 }}
47
+ {{- with .Values.workloadLabels }}
48
+ {{- toYaml . | nindent 8 }}
49
+ {{- end }}
50
+ {{- if .Values.rabbitmq.rolloutOnConfigChange }}
51
+ annotations:
52
+ checksum/config: {{ include (print $.Template.BasePath "/rabbitmq-config.yaml") . | sha256sum }}
53
+ {{- end }}
146
54
  spec:
55
+ {{- with .Values.pullSecret }}
56
+ imagePullSecrets:
57
+ - name: {{ . }}
58
+ {{- end }}
147
59
  containers:
148
60
  - name: rabbitmq
149
61
  image: {{ .Values.rabbitmq.image | quote }}
62
+ {{- with .Values.rabbitmq.imagePullPolicy }}
63
+ imagePullPolicy: {{ . }}
64
+ {{- end }}
150
65
  ports:
151
66
  - name: amqp
152
67
  containerPort: 5672
@@ -156,6 +71,18 @@ spec:
156
71
  containerPort: 61613
157
72
  - name: management
158
73
  containerPort: 15672
74
+ {{- with .Values.rabbitmq.readinessProbe }}
75
+ readinessProbe:
76
+ {{- toYaml . | nindent 12 }}
77
+ {{- end }}
78
+ {{- with .Values.rabbitmq.livenessProbe }}
79
+ livenessProbe:
80
+ {{- toYaml . | nindent 12 }}
81
+ {{- end }}
82
+ {{- with .Values.rabbitmq.resources }}
83
+ resources:
84
+ {{- toYaml . | nindent 12 }}
85
+ {{- end }}
159
86
  volumeMounts:
160
87
  - name: config
161
88
  mountPath: /etc/rabbitmq/enabled_plugins
@@ -171,10 +98,24 @@ spec:
171
98
  volumes:
172
99
  - name: config
173
100
  configMap:
174
- name: {{ .Release.Name }}-rabbitmq
101
+ name: {{ include "scada.brokerConfigName" . }}
102
+ {{- with .Values.rabbitmq.claimName }}
103
+ - name: data
104
+ persistentVolumeClaim:
105
+ claimName: {{ . }}
106
+ ---
107
+ apiVersion: v1
108
+ kind: PersistentVolumeClaim
109
+ metadata:
110
+ name: {{ . }}
111
+ namespace: {{ $.Release.Namespace }}
112
+ spec:
113
+ {{- include "scada.claim" (dict "size" $.Values.rabbitmq.storage "class" $.Values.rabbitmq.storageClass) | nindent 2 }}
114
+ {{- else }}
175
115
  volumeClaimTemplates:
176
116
  - metadata:
177
117
  name: data
178
118
  spec:
179
119
  {{- include "scada.claim" (dict "size" .Values.rabbitmq.storage "class" .Values.rabbitmq.storageClass) | nindent 8 }}
180
120
  {{- end }}
121
+ {{- end }}