@yarkivaev/scada 2.3.89 → 2.3.112
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.
- package/README.md +6 -1
- package/charts/scada/Chart.yaml +6 -0
- package/charts/scada/templates/_helpers.tpl +63 -0
- package/charts/scada/templates/clickhouse.yaml +57 -0
- package/charts/scada/templates/collector.yaml +115 -0
- package/charts/scada/templates/postgres.yaml +70 -0
- package/charts/scada/templates/rabbitmq.yaml +150 -0
- package/charts/scada/templates/site-server.yaml +110 -0
- package/charts/scada/templates/supervisor-sink.yaml +64 -0
- package/charts/scada/values.yaml +30 -0
- package/index.js +10 -0
- package/package.json +4 -2
- package/src/application/deploy/appReleases.js +50 -0
- package/src/application/deploy/applyIncludes.js +31 -0
- package/src/application/deploy/expand.js +49 -0
- package/src/application/deploy/expandDevices.js +135 -0
- package/src/application/deploy/expandFlows.js +281 -0
- package/src/application/deploy/expandNamespace.js +193 -0
- package/src/application/deploy/expandPattern.js +207 -0
- package/src/application/deploy/expandProfiles.js +70 -0
- package/src/application/deploy/frozen.js +23 -0
- package/src/application/deploy/mergeDocuments.js +98 -0
- package/src/application/deploy/paths.js +86 -0
- package/src/application/deploy/pointPaths.js +20 -0
- package/src/application/deploy/readDocument.js +169 -0
- package/src/application/deploy/releaseEdges.js +185 -0
- package/src/application/deploy/releaseNodes.js +118 -0
- package/src/application/deploy/releaseValues.js +200 -0
- package/src/application/deploy/releases.js +115 -0
- package/src/application/deploy/withCollectors.js +28 -0
- package/src/bin/modbus-to-mqtt.js +57 -0
- package/src/infrastructure/ingest/mqtt/addressWindow.js +79 -0
- package/src/infrastructure/ingest/mqtt/modbusPublish.js +277 -0
- package/src/infrastructure/ingest/mqtt/registerPoint.js +90 -0
- package/src/infrastructure/ingest/mqtt/registerPublish.js +84 -0
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ Main entry (`import { … } from '@yarkivaev/scada'`):
|
|
|
134
134
|
| `pgTimeline`, `pgAlerts`, `stateDataFromPool`, `stateDataFromMemory` | State ports |
|
|
135
135
|
| `stompTimeline`, `stompAlerts`, `userDecisions` | STOMP messaging |
|
|
136
136
|
| `machineOwners`, `ownerTimeline`, `httpTimeline`, `httpOperations` | Owner-routed timeline and operations write (local or edge HTTP) |
|
|
137
|
-
| `mqttMetrics`, `modbusMqtt`, `activityTracking`, `amqpMetricsIngest` | Ingest pipelines |
|
|
137
|
+
| `mqttMetrics`, `modbusMqtt`, `modbusPublish`, `activityTracking`, `amqpMetricsIngest` | Ingest pipelines |
|
|
138
138
|
| `analyticsMcp`, `namespaceTree`, `coverageSpans`, `coverageOverlay`, `coverageCursor`, `sqlGuard` | Analytics MCP, path index, coverage spans, timeline-name overlay, checkpoint-or-sample cursor, read-only SQL guard |
|
|
139
139
|
|
|
140
140
|
Subpath exports:
|
|
@@ -184,6 +184,7 @@ Alert and HMI copy are inject-only: pass `translations` into `siteServer` / `pla
|
|
|
184
184
|
| Binary | Role |
|
|
185
185
|
|--------|------|
|
|
186
186
|
| `supervisor-sink` | STOMP → PostgreSQL ingest + edge HTTP API (`STATE_HTTP_PORT`, default 8081) |
|
|
187
|
+
| `modbus-to-mqtt` | Poll Modbus and publish `STREAM` + `MAPPING` document paths (`src/bin/modbus-to-mqtt.js`) |
|
|
187
188
|
|
|
188
189
|
Full plant API with site-specific routes: plant packages call `siteServer` from this package.
|
|
189
190
|
|
|
@@ -223,6 +224,10 @@ Set `SINK_DB_PROFILE=central|edge`. Migrations run at supervisor-sink startup vi
|
|
|
223
224
|
| `CLICKHOUSE_URL` | Central metrics sink |
|
|
224
225
|
| `PORT` | Plant API port (siteServer, default 3000) |
|
|
225
226
|
|
|
227
|
+
## Deploy
|
|
228
|
+
|
|
229
|
+
The deployment DSL is specified in [docs/deploy.md](docs/deploy.md). A flow is `from` and `name`. An object listed on a node is state that node is responsible for. Profile, cluster, and modules are implementation. One Helm release of `charts/scada` is one cluster, cut from that document.
|
|
230
|
+
|
|
226
231
|
## Testing
|
|
227
232
|
|
|
228
233
|
```bash
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{{- define "scada.labels" -}}
|
|
2
|
+
app.kubernetes.io/name: scada
|
|
3
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
4
|
+
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
5
|
+
{{- end }}
|
|
6
|
+
|
|
7
|
+
{{- define "scada.device" -}}
|
|
8
|
+
{{- $serial := regexFind "/dev/tty[A-Za-z0-9]+" . -}}
|
|
9
|
+
{{- if $serial -}}
|
|
10
|
+
{{- $serial -}}
|
|
11
|
+
{{- else -}}
|
|
12
|
+
{{- . -}}
|
|
13
|
+
{{- end -}}
|
|
14
|
+
{{- end }}
|
|
15
|
+
|
|
16
|
+
{{- define "scada.slug" -}}
|
|
17
|
+
{{- regexReplaceAll "[^A-Za-z0-9]+" . "-" | trimAll "-" | lower -}}
|
|
18
|
+
{{- end }}
|
|
19
|
+
|
|
20
|
+
{{- define "scada.routingKey" -}}
|
|
21
|
+
{{- if eq . "*" -}}
|
|
22
|
+
#
|
|
23
|
+
{{- else if hasSuffix "/*" . -}}
|
|
24
|
+
{{- printf "%s.#" (replace "/" "." (trimSuffix "/*" .)) -}}
|
|
25
|
+
{{- else -}}
|
|
26
|
+
{{- replace "/" "." . -}}
|
|
27
|
+
{{- end -}}
|
|
28
|
+
{{- end }}
|
|
29
|
+
|
|
30
|
+
{{- define "scada.linkEnv" -}}
|
|
31
|
+
- name: SINK_DB_PROFILE
|
|
32
|
+
value: {{ .Values.role | quote }}
|
|
33
|
+
{{- if .Values.postgres.enabled }}
|
|
34
|
+
- name: POSTGRES_URL
|
|
35
|
+
valueFrom:
|
|
36
|
+
secretKeyRef:
|
|
37
|
+
name: {{ .Values.postgres.secret }}
|
|
38
|
+
key: url
|
|
39
|
+
- name: POSTGRES_ADMIN_URL
|
|
40
|
+
valueFrom:
|
|
41
|
+
secretKeyRef:
|
|
42
|
+
name: {{ .Values.postgres.secret }}
|
|
43
|
+
key: adminUrl
|
|
44
|
+
{{- end }}
|
|
45
|
+
{{- if .Values.rabbitmq.enabled }}
|
|
46
|
+
- name: STOMP_URL
|
|
47
|
+
value: {{ printf "stomp://%s-rabbitmq:61613" .Release.Name | quote }}
|
|
48
|
+
- name: STOMP_LOGIN
|
|
49
|
+
value: guest
|
|
50
|
+
- name: STOMP_PASSCODE
|
|
51
|
+
value: guest
|
|
52
|
+
- name: STOMP_HOST
|
|
53
|
+
value: /
|
|
54
|
+
- name: AMQP_URL
|
|
55
|
+
value: {{ printf "amqp://guest:guest@%s-rabbitmq:5672" .Release.Name | quote }}
|
|
56
|
+
{{- end }}
|
|
57
|
+
{{- if .Values.clickhouse.enabled }}
|
|
58
|
+
- name: CLICKHOUSE_HOST
|
|
59
|
+
value: {{ printf "%s-clickhouse" .Release.Name | quote }}
|
|
60
|
+
- name: CLICKHOUSE_URL
|
|
61
|
+
value: {{ printf "http://%s-clickhouse:8123" .Release.Name | quote }}
|
|
62
|
+
{{- end }}
|
|
63
|
+
{{- end }}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{{- if .Values.clickhouse.enabled }}
|
|
2
|
+
apiVersion: v1
|
|
3
|
+
kind: Service
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{ .Release.Name }}-clickhouse
|
|
6
|
+
namespace: {{ .Release.Namespace }}
|
|
7
|
+
labels:
|
|
8
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
9
|
+
app: clickhouse
|
|
10
|
+
spec:
|
|
11
|
+
ports:
|
|
12
|
+
- name: http
|
|
13
|
+
port: 8123
|
|
14
|
+
targetPort: http
|
|
15
|
+
selector:
|
|
16
|
+
app: clickhouse
|
|
17
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
18
|
+
---
|
|
19
|
+
apiVersion: apps/v1
|
|
20
|
+
kind: StatefulSet
|
|
21
|
+
metadata:
|
|
22
|
+
name: {{ .Release.Name }}-clickhouse
|
|
23
|
+
namespace: {{ .Release.Namespace }}
|
|
24
|
+
labels:
|
|
25
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
26
|
+
app: clickhouse
|
|
27
|
+
spec:
|
|
28
|
+
serviceName: {{ .Release.Name }}-clickhouse
|
|
29
|
+
replicas: 1
|
|
30
|
+
selector:
|
|
31
|
+
matchLabels:
|
|
32
|
+
app: clickhouse
|
|
33
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
34
|
+
template:
|
|
35
|
+
metadata:
|
|
36
|
+
labels:
|
|
37
|
+
{{- include "scada.labels" . | nindent 8 }}
|
|
38
|
+
app: clickhouse
|
|
39
|
+
spec:
|
|
40
|
+
containers:
|
|
41
|
+
- name: clickhouse
|
|
42
|
+
image: {{ .Values.clickhouse.image | quote }}
|
|
43
|
+
ports:
|
|
44
|
+
- name: http
|
|
45
|
+
containerPort: 8123
|
|
46
|
+
volumeMounts:
|
|
47
|
+
- name: data
|
|
48
|
+
mountPath: /var/lib/clickhouse
|
|
49
|
+
volumeClaimTemplates:
|
|
50
|
+
- metadata:
|
|
51
|
+
name: data
|
|
52
|
+
spec:
|
|
53
|
+
accessModes: ["ReadWriteOnce"]
|
|
54
|
+
resources:
|
|
55
|
+
requests:
|
|
56
|
+
storage: 20Gi
|
|
57
|
+
{{- end }}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{{- if .Values.collectors }}
|
|
2
|
+
{{- $groups := dict -}}
|
|
3
|
+
{{- range $item := .Values.collectors }}
|
|
4
|
+
{{- $device := include "scada.device" $item.address | trim -}}
|
|
5
|
+
{{- if not $device }}
|
|
6
|
+
{{- fail "collector has no address" }}
|
|
7
|
+
{{- end }}
|
|
8
|
+
{{- if not $item.path }}
|
|
9
|
+
{{- fail (printf "collector %s has no path" $device) }}
|
|
10
|
+
{{- end }}
|
|
11
|
+
{{- if not $item.command }}
|
|
12
|
+
{{- fail (printf "collector %s has no command" $device) }}
|
|
13
|
+
{{- end }}
|
|
14
|
+
{{- $token := $item.command | toJson -}}
|
|
15
|
+
{{- $prior := list -}}
|
|
16
|
+
{{- if hasKey $groups $token }}
|
|
17
|
+
{{- $prior = index $groups $token -}}
|
|
18
|
+
{{- end }}
|
|
19
|
+
{{- $groups = set $groups $token (append $prior $item) -}}
|
|
20
|
+
{{- end }}
|
|
21
|
+
{{- $claimed := dict -}}
|
|
22
|
+
{{- range $token, $items := $groups }}
|
|
23
|
+
{{- range $item := $items }}
|
|
24
|
+
{{- $device := include "scada.device" $item.address | trim -}}
|
|
25
|
+
{{- if hasPrefix "/dev/" $device }}
|
|
26
|
+
{{- if and (hasKey $claimed $device) (ne (index $claimed $device) $token) }}
|
|
27
|
+
{{- fail (printf "collectors on %s use different commands" $device) }}
|
|
28
|
+
{{- end }}
|
|
29
|
+
{{- $claimed = set $claimed $device $token -}}
|
|
30
|
+
{{- end }}
|
|
31
|
+
{{- end }}
|
|
32
|
+
{{- end }}
|
|
33
|
+
{{- range $token, $items := $groups }}
|
|
34
|
+
{{- $argv := (index $items 0).command -}}
|
|
35
|
+
{{- $slug := include "scada.slug" (index $argv (sub (len $argv) 1)) | trim -}}
|
|
36
|
+
{{- if not $slug }}
|
|
37
|
+
{{- fail (printf "collector %s has no name" $token) }}
|
|
38
|
+
{{- end }}
|
|
39
|
+
{{- $paths := list -}}
|
|
40
|
+
{{- $maps := list -}}
|
|
41
|
+
{{- $addresses := list -}}
|
|
42
|
+
{{- $serials := list -}}
|
|
43
|
+
{{- range $item := $items }}
|
|
44
|
+
{{- $paths = append $paths $item.path -}}
|
|
45
|
+
{{- $maps = append $maps ($item.mapping | default (dict)) -}}
|
|
46
|
+
{{- $addresses = append $addresses $item.address -}}
|
|
47
|
+
{{- $device := include "scada.device" $item.address | trim -}}
|
|
48
|
+
{{- if and (hasPrefix "/dev/" $device) (not (has $device $serials)) }}
|
|
49
|
+
{{- $serials = append $serials $device -}}
|
|
50
|
+
{{- end }}
|
|
51
|
+
{{- end }}
|
|
52
|
+
---
|
|
53
|
+
apiVersion: apps/v1
|
|
54
|
+
kind: Deployment
|
|
55
|
+
metadata:
|
|
56
|
+
name: {{ $.Release.Name }}-collector-{{ $slug }}
|
|
57
|
+
namespace: {{ $.Release.Namespace }}
|
|
58
|
+
labels:
|
|
59
|
+
{{- include "scada.labels" $ | nindent 4 }}
|
|
60
|
+
app: collector
|
|
61
|
+
spec:
|
|
62
|
+
replicas: 1
|
|
63
|
+
selector:
|
|
64
|
+
matchLabels:
|
|
65
|
+
app: collector
|
|
66
|
+
app.kubernetes.io/instance: {{ $.Release.Name }}
|
|
67
|
+
scada/command: {{ $slug }}
|
|
68
|
+
template:
|
|
69
|
+
metadata:
|
|
70
|
+
labels:
|
|
71
|
+
{{- include "scada.labels" $ | nindent 8 }}
|
|
72
|
+
app: collector
|
|
73
|
+
scada/command: {{ $slug }}
|
|
74
|
+
spec:
|
|
75
|
+
{{- with $.Values.pullSecret }}
|
|
76
|
+
imagePullSecrets:
|
|
77
|
+
- name: {{ . }}
|
|
78
|
+
{{- end }}
|
|
79
|
+
containers:
|
|
80
|
+
- name: collector
|
|
81
|
+
image: {{ required "image is required" $.Values.image | quote }}
|
|
82
|
+
command: {{ $argv | toJson }}
|
|
83
|
+
{{- if $serials }}
|
|
84
|
+
securityContext:
|
|
85
|
+
privileged: true
|
|
86
|
+
volumeMounts:
|
|
87
|
+
{{- range $serials }}
|
|
88
|
+
- name: {{ include "scada.slug" . | trim }}
|
|
89
|
+
mountPath: {{ . }}
|
|
90
|
+
{{- end }}
|
|
91
|
+
{{- end }}
|
|
92
|
+
env:
|
|
93
|
+
- name: ADDRESS
|
|
94
|
+
value: {{ $addresses | toJson | quote }}
|
|
95
|
+
- name: STREAM
|
|
96
|
+
value: {{ $paths | toJson | quote }}
|
|
97
|
+
- name: MAPPING
|
|
98
|
+
value: {{ $maps | toJson | quote }}
|
|
99
|
+
{{- if $.Values.rabbitmq.enabled }}
|
|
100
|
+
- name: MQTT_URL
|
|
101
|
+
value: {{ printf "mqtt://%s-rabbitmq:1883" $.Release.Name | quote }}
|
|
102
|
+
{{- end }}
|
|
103
|
+
resources:
|
|
104
|
+
{{- toYaml $.Values.resources | nindent 12 }}
|
|
105
|
+
{{- if $serials }}
|
|
106
|
+
volumes:
|
|
107
|
+
{{- range $serials }}
|
|
108
|
+
- name: {{ include "scada.slug" . | trim }}
|
|
109
|
+
hostPath:
|
|
110
|
+
path: {{ . }}
|
|
111
|
+
type: CharDevice
|
|
112
|
+
{{- end }}
|
|
113
|
+
{{- end }}
|
|
114
|
+
{{- end }}
|
|
115
|
+
{{- end }}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{{- if .Values.postgres.enabled }}
|
|
2
|
+
apiVersion: v1
|
|
3
|
+
kind: Service
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{ .Release.Name }}-postgres
|
|
6
|
+
namespace: {{ .Release.Namespace }}
|
|
7
|
+
labels:
|
|
8
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
9
|
+
app: postgres
|
|
10
|
+
spec:
|
|
11
|
+
ports:
|
|
12
|
+
- name: postgres
|
|
13
|
+
port: 5432
|
|
14
|
+
targetPort: postgres
|
|
15
|
+
selector:
|
|
16
|
+
app: postgres
|
|
17
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
18
|
+
---
|
|
19
|
+
apiVersion: apps/v1
|
|
20
|
+
kind: StatefulSet
|
|
21
|
+
metadata:
|
|
22
|
+
name: {{ .Release.Name }}-postgres
|
|
23
|
+
namespace: {{ .Release.Namespace }}
|
|
24
|
+
labels:
|
|
25
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
26
|
+
app: postgres
|
|
27
|
+
spec:
|
|
28
|
+
serviceName: {{ .Release.Name }}-postgres
|
|
29
|
+
replicas: 1
|
|
30
|
+
selector:
|
|
31
|
+
matchLabels:
|
|
32
|
+
app: postgres
|
|
33
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
34
|
+
template:
|
|
35
|
+
metadata:
|
|
36
|
+
labels:
|
|
37
|
+
{{- include "scada.labels" . | nindent 8 }}
|
|
38
|
+
app: postgres
|
|
39
|
+
spec:
|
|
40
|
+
containers:
|
|
41
|
+
- name: postgres
|
|
42
|
+
image: {{ .Values.postgres.image | quote }}
|
|
43
|
+
ports:
|
|
44
|
+
- name: postgres
|
|
45
|
+
containerPort: 5432
|
|
46
|
+
env:
|
|
47
|
+
- name: POSTGRES_DB
|
|
48
|
+
value: {{ .Values.postgres.database | quote }}
|
|
49
|
+
- name: POSTGRES_USER
|
|
50
|
+
valueFrom:
|
|
51
|
+
secretKeyRef:
|
|
52
|
+
name: {{ .Values.postgres.secret }}
|
|
53
|
+
key: user
|
|
54
|
+
- name: POSTGRES_PASSWORD
|
|
55
|
+
valueFrom:
|
|
56
|
+
secretKeyRef:
|
|
57
|
+
name: {{ .Values.postgres.secret }}
|
|
58
|
+
key: password
|
|
59
|
+
volumeMounts:
|
|
60
|
+
- name: data
|
|
61
|
+
mountPath: /var/lib/postgresql/data
|
|
62
|
+
volumeClaimTemplates:
|
|
63
|
+
- metadata:
|
|
64
|
+
name: data
|
|
65
|
+
spec:
|
|
66
|
+
accessModes: ["ReadWriteOnce"]
|
|
67
|
+
resources:
|
|
68
|
+
requests:
|
|
69
|
+
storage: 10Gi
|
|
70
|
+
{{- end }}
|
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
{{- $binding := list -}}
|
|
39
|
+
{{- range $key := $ingest }}
|
|
40
|
+
{{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.ingest" "destination_type" "queue" "routing_key" $key) -}}
|
|
41
|
+
{{- end }}
|
|
42
|
+
{{- range $pair := (list
|
|
43
|
+
(dict "source" "scada.operations" "destination" "scada.operations.ingest")
|
|
44
|
+
(dict "source" "scada.segments" "destination" "scada.segments.ingest")
|
|
45
|
+
(dict "source" "scada.user_decisions" "destination" "scada.user_decisions")) }}
|
|
46
|
+
{{- $binding = append $binding (dict "source" $pair.source "vhost" "/" "destination" $pair.destination "destination_type" "queue" "routing_key" "#") -}}
|
|
47
|
+
{{- end }}
|
|
48
|
+
{{- range $key := $outgoing }}
|
|
49
|
+
{{- $binding = append $binding (dict "source" "scada.telemetry" "vhost" "/" "destination" "scada.telemetry.outbox" "destination_type" "queue" "routing_key" $key) -}}
|
|
50
|
+
{{- end }}
|
|
51
|
+
{{- $definitions := dict
|
|
52
|
+
"vhosts" (list (dict "name" "/"))
|
|
53
|
+
"users" (list (dict "name" "guest" "password" "guest" "tags" (list "administrator")))
|
|
54
|
+
"permissions" (list (dict "user" "guest" "vhost" "/" "configure" ".*" "write" ".*" "read" ".*"))
|
|
55
|
+
"exchanges" $exchange
|
|
56
|
+
"queues" $queue
|
|
57
|
+
"bindings" $binding
|
|
58
|
+
-}}
|
|
59
|
+
apiVersion: v1
|
|
60
|
+
kind: ConfigMap
|
|
61
|
+
metadata:
|
|
62
|
+
name: {{ .Release.Name }}-rabbitmq
|
|
63
|
+
namespace: {{ .Release.Namespace }}
|
|
64
|
+
labels:
|
|
65
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
66
|
+
app: rabbitmq
|
|
67
|
+
data:
|
|
68
|
+
enabled_plugins: |
|
|
69
|
+
[rabbitmq_stomp, rabbitmq_mqtt, rabbitmq_management].
|
|
70
|
+
rabbitmq.conf: |
|
|
71
|
+
load_definitions = /etc/rabbitmq/definitions.json
|
|
72
|
+
mqtt.listeners.tcp.default = 1883
|
|
73
|
+
mqtt.allow_anonymous = true
|
|
74
|
+
mqtt.vhost = /
|
|
75
|
+
mqtt.exchange = scada.telemetry
|
|
76
|
+
definitions.json: |
|
|
77
|
+
{{ $definitions | toPrettyJson | nindent 4 }}
|
|
78
|
+
---
|
|
79
|
+
apiVersion: v1
|
|
80
|
+
kind: Service
|
|
81
|
+
metadata:
|
|
82
|
+
name: {{ .Release.Name }}-rabbitmq
|
|
83
|
+
namespace: {{ .Release.Namespace }}
|
|
84
|
+
labels:
|
|
85
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
86
|
+
app: rabbitmq
|
|
87
|
+
spec:
|
|
88
|
+
ports:
|
|
89
|
+
- name: amqp
|
|
90
|
+
port: 5672
|
|
91
|
+
targetPort: amqp
|
|
92
|
+
- name: mqtt
|
|
93
|
+
port: 1883
|
|
94
|
+
targetPort: mqtt
|
|
95
|
+
- name: stomp
|
|
96
|
+
port: 61613
|
|
97
|
+
targetPort: stomp
|
|
98
|
+
selector:
|
|
99
|
+
app: rabbitmq
|
|
100
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
101
|
+
---
|
|
102
|
+
apiVersion: apps/v1
|
|
103
|
+
kind: StatefulSet
|
|
104
|
+
metadata:
|
|
105
|
+
name: {{ .Release.Name }}-rabbitmq
|
|
106
|
+
namespace: {{ .Release.Namespace }}
|
|
107
|
+
labels:
|
|
108
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
109
|
+
app: rabbitmq
|
|
110
|
+
spec:
|
|
111
|
+
serviceName: {{ .Release.Name }}-rabbitmq
|
|
112
|
+
replicas: 1
|
|
113
|
+
selector:
|
|
114
|
+
matchLabels:
|
|
115
|
+
app: rabbitmq
|
|
116
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
117
|
+
template:
|
|
118
|
+
metadata:
|
|
119
|
+
labels:
|
|
120
|
+
{{- include "scada.labels" . | nindent 8 }}
|
|
121
|
+
app: rabbitmq
|
|
122
|
+
spec:
|
|
123
|
+
containers:
|
|
124
|
+
- name: rabbitmq
|
|
125
|
+
image: {{ .Values.rabbitmq.image | quote }}
|
|
126
|
+
ports:
|
|
127
|
+
- name: amqp
|
|
128
|
+
containerPort: 5672
|
|
129
|
+
- name: mqtt
|
|
130
|
+
containerPort: 1883
|
|
131
|
+
- name: stomp
|
|
132
|
+
containerPort: 61613
|
|
133
|
+
volumeMounts:
|
|
134
|
+
- name: config
|
|
135
|
+
mountPath: /etc/rabbitmq
|
|
136
|
+
- name: data
|
|
137
|
+
mountPath: /var/lib/rabbitmq
|
|
138
|
+
volumes:
|
|
139
|
+
- name: config
|
|
140
|
+
configMap:
|
|
141
|
+
name: {{ .Release.Name }}-rabbitmq
|
|
142
|
+
volumeClaimTemplates:
|
|
143
|
+
- metadata:
|
|
144
|
+
name: data
|
|
145
|
+
spec:
|
|
146
|
+
accessModes: ["ReadWriteOnce"]
|
|
147
|
+
resources:
|
|
148
|
+
requests:
|
|
149
|
+
storage: 5Gi
|
|
150
|
+
{{- end }}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{ .Release.Name }}-site-server
|
|
5
|
+
namespace: {{ .Release.Namespace }}
|
|
6
|
+
labels:
|
|
7
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
8
|
+
app: site-server
|
|
9
|
+
spec:
|
|
10
|
+
replicas: 1
|
|
11
|
+
selector:
|
|
12
|
+
matchLabels:
|
|
13
|
+
app: site-server
|
|
14
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
15
|
+
template:
|
|
16
|
+
metadata:
|
|
17
|
+
labels:
|
|
18
|
+
{{- include "scada.labels" . | nindent 8 }}
|
|
19
|
+
app: site-server
|
|
20
|
+
spec:
|
|
21
|
+
{{- with .Values.pullSecret }}
|
|
22
|
+
imagePullSecrets:
|
|
23
|
+
- name: {{ . }}
|
|
24
|
+
{{- end }}
|
|
25
|
+
containers:
|
|
26
|
+
- name: site-server
|
|
27
|
+
image: {{ required "image is required" .Values.image | quote }}
|
|
28
|
+
{{- with .Values.command }}
|
|
29
|
+
command: {{ toJson . }}
|
|
30
|
+
{{- end }}
|
|
31
|
+
ports:
|
|
32
|
+
- name: http
|
|
33
|
+
containerPort: {{ .Values.service.port }}
|
|
34
|
+
- name: internal-api
|
|
35
|
+
containerPort: {{ .Values.service.internalPort }}
|
|
36
|
+
env:
|
|
37
|
+
- name: PORT
|
|
38
|
+
value: {{ .Values.service.port | quote }}
|
|
39
|
+
- name: STATE_HTTP_PORT
|
|
40
|
+
value: {{ .Values.service.internalPort | quote }}
|
|
41
|
+
{{- include "scada.linkEnv" . | nindent 12 }}
|
|
42
|
+
- name: SINK_PIPELINES
|
|
43
|
+
value: "false"
|
|
44
|
+
{{- if .Values.machines }}
|
|
45
|
+
- name: EDGE_MACHINES
|
|
46
|
+
value: {{ .Values.machines | toJson | quote }}
|
|
47
|
+
{{- end }}
|
|
48
|
+
{{- if .Values.sites }}
|
|
49
|
+
- name: EDGE_SITES
|
|
50
|
+
value: {{ .Values.sites | toJson | quote }}
|
|
51
|
+
{{- end }}
|
|
52
|
+
{{- range .Values.env }}
|
|
53
|
+
- name: {{ .name }}
|
|
54
|
+
value: {{ .value | quote }}
|
|
55
|
+
{{- end }}
|
|
56
|
+
readinessProbe:
|
|
57
|
+
httpGet:
|
|
58
|
+
path: /api/v1/machines
|
|
59
|
+
port: http
|
|
60
|
+
initialDelaySeconds: 5
|
|
61
|
+
periodSeconds: 10
|
|
62
|
+
livenessProbe:
|
|
63
|
+
httpGet:
|
|
64
|
+
path: /api/v1/machines
|
|
65
|
+
port: http
|
|
66
|
+
initialDelaySeconds: 15
|
|
67
|
+
periodSeconds: 30
|
|
68
|
+
resources:
|
|
69
|
+
{{- toYaml .Values.resources | nindent 12 }}
|
|
70
|
+
---
|
|
71
|
+
apiVersion: v1
|
|
72
|
+
kind: Service
|
|
73
|
+
metadata:
|
|
74
|
+
name: {{ .Release.Name }}-site-server
|
|
75
|
+
namespace: {{ .Release.Namespace }}
|
|
76
|
+
labels:
|
|
77
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
78
|
+
app: site-server
|
|
79
|
+
spec:
|
|
80
|
+
ports:
|
|
81
|
+
- name: http
|
|
82
|
+
port: {{ .Values.service.port }}
|
|
83
|
+
targetPort: http
|
|
84
|
+
- name: internal-api
|
|
85
|
+
port: {{ .Values.service.internalPort }}
|
|
86
|
+
targetPort: internal-api
|
|
87
|
+
selector:
|
|
88
|
+
app: site-server
|
|
89
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
90
|
+
{{- if gt (int .Values.service.nodePort) 0 }}
|
|
91
|
+
---
|
|
92
|
+
apiVersion: v1
|
|
93
|
+
kind: Service
|
|
94
|
+
metadata:
|
|
95
|
+
name: {{ .Release.Name }}-site-server-plant-lan
|
|
96
|
+
namespace: {{ .Release.Namespace }}
|
|
97
|
+
labels:
|
|
98
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
99
|
+
app: site-server
|
|
100
|
+
spec:
|
|
101
|
+
type: NodePort
|
|
102
|
+
ports:
|
|
103
|
+
- name: http
|
|
104
|
+
port: {{ .Values.service.port }}
|
|
105
|
+
targetPort: http
|
|
106
|
+
nodePort: {{ .Values.service.nodePort }}
|
|
107
|
+
selector:
|
|
108
|
+
app: site-server
|
|
109
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
110
|
+
{{- end }}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{ .Release.Name }}-supervisor-sink
|
|
5
|
+
namespace: {{ .Release.Namespace }}
|
|
6
|
+
labels:
|
|
7
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
8
|
+
app: supervisor-sink
|
|
9
|
+
spec:
|
|
10
|
+
replicas: 1
|
|
11
|
+
selector:
|
|
12
|
+
matchLabels:
|
|
13
|
+
app: supervisor-sink
|
|
14
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
15
|
+
template:
|
|
16
|
+
metadata:
|
|
17
|
+
labels:
|
|
18
|
+
{{- include "scada.labels" . | nindent 8 }}
|
|
19
|
+
app: supervisor-sink
|
|
20
|
+
spec:
|
|
21
|
+
{{- with .Values.pullSecret }}
|
|
22
|
+
imagePullSecrets:
|
|
23
|
+
- name: {{ . }}
|
|
24
|
+
{{- end }}
|
|
25
|
+
containers:
|
|
26
|
+
- name: supervisor-sink
|
|
27
|
+
image: {{ required "image is required" .Values.image | quote }}
|
|
28
|
+
command: ["node", "node_modules/@yarkivaev/scada/src/bin/supervisor-sink.js"]
|
|
29
|
+
ports:
|
|
30
|
+
- name: state
|
|
31
|
+
containerPort: {{ .Values.service.internalPort }}
|
|
32
|
+
env:
|
|
33
|
+
- name: STATE_HTTP_PORT
|
|
34
|
+
value: {{ .Values.service.internalPort | quote }}
|
|
35
|
+
{{- include "scada.linkEnv" . | nindent 12 }}
|
|
36
|
+
readinessProbe:
|
|
37
|
+
tcpSocket:
|
|
38
|
+
port: state
|
|
39
|
+
initialDelaySeconds: 5
|
|
40
|
+
periodSeconds: 10
|
|
41
|
+
livenessProbe:
|
|
42
|
+
tcpSocket:
|
|
43
|
+
port: state
|
|
44
|
+
initialDelaySeconds: 15
|
|
45
|
+
periodSeconds: 30
|
|
46
|
+
resources:
|
|
47
|
+
{{- toYaml .Values.resources | nindent 12 }}
|
|
48
|
+
---
|
|
49
|
+
apiVersion: v1
|
|
50
|
+
kind: Service
|
|
51
|
+
metadata:
|
|
52
|
+
name: {{ .Release.Name }}-supervisor-sink
|
|
53
|
+
namespace: {{ .Release.Namespace }}
|
|
54
|
+
labels:
|
|
55
|
+
{{- include "scada.labels" . | nindent 4 }}
|
|
56
|
+
app: supervisor-sink
|
|
57
|
+
spec:
|
|
58
|
+
ports:
|
|
59
|
+
- name: state
|
|
60
|
+
port: {{ .Values.service.internalPort }}
|
|
61
|
+
targetPort: state
|
|
62
|
+
selector:
|
|
63
|
+
app: supervisor-sink
|
|
64
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
role: edge
|
|
2
|
+
image: ""
|
|
3
|
+
command: []
|
|
4
|
+
pullSecret: ""
|
|
5
|
+
machines: []
|
|
6
|
+
sites: []
|
|
7
|
+
collectors: []
|
|
8
|
+
env: []
|
|
9
|
+
service:
|
|
10
|
+
port: 3000
|
|
11
|
+
internalPort: 8081
|
|
12
|
+
nodePort: 0
|
|
13
|
+
postgres:
|
|
14
|
+
enabled: true
|
|
15
|
+
image: postgres:16
|
|
16
|
+
secret: scada-postgres
|
|
17
|
+
database: scada
|
|
18
|
+
rabbitmq:
|
|
19
|
+
enabled: true
|
|
20
|
+
image: rabbitmq:3.13-management
|
|
21
|
+
clickhouse:
|
|
22
|
+
enabled: false
|
|
23
|
+
image: clickhouse/clickhouse-server:24.8
|
|
24
|
+
resources:
|
|
25
|
+
requests:
|
|
26
|
+
cpu: 100m
|
|
27
|
+
memory: 128Mi
|
|
28
|
+
limits:
|
|
29
|
+
cpu: "1"
|
|
30
|
+
memory: 512Mi
|