@tetherto/mdk-core 0.0.0 → 0.12.1
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/LICENSE +202 -0
- package/README.md +572 -0
- package/index.js +519 -0
- package/lib/address-policy.js +124 -0
- package/lib/local-discovery.js +321 -0
- package/lib/services/actions.service.js +68 -0
- package/lib/services/alerts.service.js +207 -0
- package/lib/services/comments.service.js +118 -0
- package/lib/services/log-history.service.js +307 -0
- package/lib/services/logs.service.js +249 -0
- package/lib/services/pool-utils/constants.js +20 -0
- package/lib/services/pool-utils/time.js +49 -0
- package/lib/services/pool.service.js +397 -0
- package/lib/services/provisioning.service.js +420 -0
- package/lib/services/settings.service.js +26 -0
- package/lib/services/snaps.service.js +181 -0
- package/lib/services/stats.service.js +205 -0
- package/lib/stats/README.md +80 -0
- package/lib/stats/index.js +57 -0
- package/lib/stats/ops/alerts_aggr.js +40 -0
- package/lib/stats/ops/alerts_group_cnt.js +43 -0
- package/lib/stats/ops/arr_concat.js +27 -0
- package/lib/stats/ops/array_obj_calc.js +41 -0
- package/lib/stats/ops/avg.js +42 -0
- package/lib/stats/ops/cnt.js +19 -0
- package/lib/stats/ops/group.js +38 -0
- package/lib/stats/ops/group_avg.js +53 -0
- package/lib/stats/ops/group_cnt.js +38 -0
- package/lib/stats/ops/group_max.js +40 -0
- package/lib/stats/ops/group_multiple_stats.js +40 -0
- package/lib/stats/ops/group_sum.js +40 -0
- package/lib/stats/ops/index.js +35 -0
- package/lib/stats/ops/nested_obj_concat.js +54 -0
- package/lib/stats/ops/obj_concat.js +33 -0
- package/lib/stats/ops/sum.js +27 -0
- package/lib/stats/utils.js +99 -0
- package/lib/templates/alerts.js +128 -0
- package/lib/templates/stats.js +476 -0
- package/lib/things/constants.js +65 -0
- package/lib/things/container.js +84 -0
- package/lib/things/miner.js +190 -0
- package/lib/things/powermeter.js +30 -0
- package/lib/things/sensor.js +18 -0
- package/lib/things/thing.js +93 -0
- package/lib/utils.js +603 -0
- package/lib/worker-infra.js +221 -0
- package/package.json +64 -1
- package/services.js +246 -0
- package/utils/compose-yaml.js +56 -0
- package/utils/constants.js +30 -0
- package/utils/index.js +29 -0
- package/utils/initialize.js +148 -0
- package/utils/service-bootstrap.js +271 -0
- package/worker.js +9 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { MDK_ROOT, LIB_TYPES } = require('./constants')
|
|
6
|
+
|
|
7
|
+
// The gateway worker is package code: resolved by name, never by its
|
|
8
|
+
// position in a checkout.
|
|
9
|
+
const gatewayPackageDir = () => path.dirname(require.resolve('@tetherto/mdk-gateway/package.json'))
|
|
10
|
+
|
|
11
|
+
const thingConfigSources = (localPackages) => [
|
|
12
|
+
{
|
|
13
|
+
destDir: 'types/AvalonMinerManagerA1346/config',
|
|
14
|
+
srcDirs: [path.join(localPackages, LIB_TYPES.AVALON, 'config')]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
destDir: 'types/BitdeerManagerD40M56/config',
|
|
18
|
+
srcDirs: [path.join(localPackages, LIB_TYPES.BITDEER, 'config')]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
destDir: 'types/AnstspaceManagerHK3/config',
|
|
22
|
+
srcDirs: [path.join(localPackages, LIB_TYPES.ANTSPACE, 'config')]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
destDir: 'types/B23PowerMeterManager/config',
|
|
26
|
+
srcDirs: [path.join(localPackages, LIB_TYPES.ABB, 'config')]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
destDir: 'types/TempSenecaSensorManager/config',
|
|
30
|
+
srcDirs: [path.join(localPackages, LIB_TYPES.SENECA, 'config')]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// Points the tmp tree's worker stub at the installed @tetherto/mdk-gateway
|
|
35
|
+
// worker, relative to <root>/<MDK_ROOT>/workers (in the monorepo the
|
|
36
|
+
// workspace link resolves to backend/core/gateway).
|
|
37
|
+
const httpNodeWrkStub = (rootStoreDir) => {
|
|
38
|
+
const target = path.join(gatewayPackageDir(), 'workers', 'http.node.wrk')
|
|
39
|
+
let rel = path.relative(path.join(rootStoreDir, 'workers'), target).split(path.sep).join('/')
|
|
40
|
+
if (!rel.startsWith('.')) rel = `./${rel}`
|
|
41
|
+
return `module.exports = require(${JSON.stringify(rel).replace(/"/g, "'")})\n`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const TEMPLATE = {
|
|
45
|
+
dirs: [
|
|
46
|
+
'config/facs',
|
|
47
|
+
'db',
|
|
48
|
+
'types/AvalonMinerManagerA1346/config',
|
|
49
|
+
'types/BitdeerManagerD40M56/config',
|
|
50
|
+
'types/AnstspaceManagerHK3/config',
|
|
51
|
+
'types/B23PowerMeterManager/config',
|
|
52
|
+
'types/TempSenecaSensorManager/config',
|
|
53
|
+
'workers'
|
|
54
|
+
],
|
|
55
|
+
files: {
|
|
56
|
+
'workers/http.node.wrk.js': httpNodeWrkStub
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const resolveSource = (dir, filename) => {
|
|
61
|
+
const plain = path.join(dir, filename)
|
|
62
|
+
const example = path.join(dir, `${filename}.example`)
|
|
63
|
+
if (fs.existsSync(plain)) return plain
|
|
64
|
+
if (fs.existsSync(example)) return example
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ensureThingConfigs = (rootStoreDir, localPackages) => {
|
|
69
|
+
for (const { destDir, srcDirs } of thingConfigSources(localPackages)) {
|
|
70
|
+
const configDir = path.join(rootStoreDir, destDir)
|
|
71
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
72
|
+
const dest = path.join(configDir, 'base.thing.json')
|
|
73
|
+
if (!fs.existsSync(dest)) {
|
|
74
|
+
const src = (srcDirs || []).reduce((found, dir) => {
|
|
75
|
+
if (found) return found
|
|
76
|
+
return resolveSource(dir, 'base.thing.json')
|
|
77
|
+
}, null)
|
|
78
|
+
if (src) fs.copyFileSync(src, dest)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Seeds the `<root>/tmp` config tree from the MDK monorepo's example configs.
|
|
85
|
+
*
|
|
86
|
+
* opts.sources MDK monorepo checkout to read configs from (the dir holding
|
|
87
|
+
* `backend/core/kernel`, `backend/core/gateway`, ...). Required:
|
|
88
|
+
* it is never guessed from this package's location.
|
|
89
|
+
* opts.root where the `tmp` tree is created (default: opts.sources).
|
|
90
|
+
*/
|
|
91
|
+
module.exports = (opts = {}) => {
|
|
92
|
+
if (typeof opts.sources !== 'string' || !opts.sources.trim()) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
'ERR_MDK_INIT_SOURCES_REQUIRED: initialize() needs opts.sources — the MDK monorepo checkout to seed config from'
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
const sources = path.resolve(opts.sources)
|
|
98
|
+
const localPackages = path.join(sources, 'backend')
|
|
99
|
+
const orkConfDir = path.join(localPackages, LIB_TYPES.Kernel, 'config')
|
|
100
|
+
const gatewayConfigsDir = path.join(localPackages, LIB_TYPES.GATEWAY, 'config', 'facs')
|
|
101
|
+
if (!fs.existsSync(orkConfDir) || !fs.existsSync(gatewayConfigsDir)) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`ERR_MDK_INIT_SOURCES_NOT_FOUND: no MDK monorepo configs under ${sources} — ` +
|
|
104
|
+
'initialize() seeds from a source checkout; pass opts.sources pointing at one'
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
const rootStoreDir = path.join(path.resolve(opts.root || sources), MDK_ROOT)
|
|
108
|
+
|
|
109
|
+
for (const dir of TEMPLATE.dirs) {
|
|
110
|
+
fs.mkdirSync(path.join(rootStoreDir, dir), { recursive: true })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const commonDest = path.join(rootStoreDir, 'config', 'common.json')
|
|
114
|
+
if (!fs.existsSync(commonDest)) {
|
|
115
|
+
const src = resolveSource(path.join(localPackages, LIB_TYPES.GATEWAY, 'config'), 'common.json')
|
|
116
|
+
if (src) fs.copyFileSync(src, commonDest)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const entry of fs.readdirSync(orkConfDir)) {
|
|
120
|
+
if (!entry.endsWith('.example')) continue
|
|
121
|
+
const filename = entry.slice(0, -'.example'.length)
|
|
122
|
+
const dest = path.join(rootStoreDir, 'config', filename)
|
|
123
|
+
if (!fs.existsSync(dest)) {
|
|
124
|
+
fs.copyFileSync(path.join(orkConfDir, entry), dest)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const entry of fs.readdirSync(gatewayConfigsDir)) {
|
|
129
|
+
if (!entry.endsWith('.example')) continue
|
|
130
|
+
const filename = entry.slice(0, -'.example'.length)
|
|
131
|
+
const dest = path.join(rootStoreDir, 'config', 'facs', filename)
|
|
132
|
+
if (!fs.existsSync(dest)) {
|
|
133
|
+
fs.copyFileSync(path.join(gatewayConfigsDir, entry), dest)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const [relPath, content] of Object.entries(TEMPLATE.files)) {
|
|
138
|
+
const filePath = path.join(rootStoreDir, relPath)
|
|
139
|
+
if (!fs.existsSync(filePath)) {
|
|
140
|
+
fs.writeFileSync(filePath, typeof content === 'function' ? content(rootStoreDir) : content, 'utf8')
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (fs.existsSync(rootStoreDir)) {
|
|
146
|
+
ensureThingConfigs(rootStoreDir, localPackages)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const crypto = require('crypto')
|
|
6
|
+
const { spawn } = require('child_process')
|
|
7
|
+
|
|
8
|
+
// Code is never located by walking up the directory tree: MDK packages are
|
|
9
|
+
// resolved by name (node_modules), and the one thing that still needs the
|
|
10
|
+
// source checkout — seeding config from its example files — takes it from
|
|
11
|
+
// MDK_REPO_ROOT, which startServices writes into the generated PM2/compose
|
|
12
|
+
// config.
|
|
13
|
+
function resolveRepoRoot (env = process.env) {
|
|
14
|
+
const root = env.MDK_REPO_ROOT
|
|
15
|
+
if (typeof root !== 'string' || !root.trim()) {
|
|
16
|
+
throw new Error('ERR_MDK_REPO_ROOT_REQUIRED: set MDK_REPO_ROOT to the MDK source checkout (startServices({ repoRoot }) writes it into the generated PM2/compose config)')
|
|
17
|
+
}
|
|
18
|
+
return path.resolve(root)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function gatewayPackageDir () {
|
|
22
|
+
return path.dirname(require.resolve('@tetherto/mdk-gateway/package.json'))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ensureConfigFromExamples (dir) {
|
|
26
|
+
if (!fs.existsSync(dir)) return
|
|
27
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
28
|
+
const full = path.join(dir, entry.name)
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
ensureConfigFromExamples(full)
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
if (!entry.name.endsWith('.example')) continue
|
|
34
|
+
const dest = path.join(dir, entry.name.slice(0, -'.example'.length))
|
|
35
|
+
if (!fs.existsSync(dest)) fs.copyFileSync(full, dest)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Every worker family boots through its package's runtime plugin boot,
|
|
40
|
+
// resolved by package name.
|
|
41
|
+
// TYPE (env) maps onto the boot's model id; workerId is
|
|
42
|
+
// `<prefix>-<model>-<rack>` so persistent stores and RPC seeds are stable
|
|
43
|
+
// per rack. Pool workers take no model — one logical pool per rack.
|
|
44
|
+
const WORKER_BOOTS = {
|
|
45
|
+
'miner-avalon': {
|
|
46
|
+
pkg: '@tetherto/mdk-worker-avalon',
|
|
47
|
+
factory: 'startAvalonWorker',
|
|
48
|
+
prefix: 'avalon',
|
|
49
|
+
models: { A1346: 'a1346' }
|
|
50
|
+
},
|
|
51
|
+
'container-antspace': {
|
|
52
|
+
pkg: '@tetherto/mdk-worker-antspace',
|
|
53
|
+
factory: 'startAntspaceWorker',
|
|
54
|
+
prefix: 'antspace',
|
|
55
|
+
models: { HK3: 'hk3', IMMERSION: 'immersion' }
|
|
56
|
+
},
|
|
57
|
+
'container-bitdeer': {
|
|
58
|
+
pkg: '@tetherto/mdk-worker-bitdeer',
|
|
59
|
+
factory: 'startBitdeerWorker',
|
|
60
|
+
prefix: 'bitdeer',
|
|
61
|
+
models: { D40_A1346: 'a1346', D40_M30: 'm30', D40_M56: 'm56', D40_S19XP: 's19xp' }
|
|
62
|
+
},
|
|
63
|
+
'powermeter-abb': {
|
|
64
|
+
pkg: '@tetherto/mdk-worker-abb',
|
|
65
|
+
factory: 'startAbbWorker',
|
|
66
|
+
prefix: 'abb',
|
|
67
|
+
models: { B23: 'b23', B24: 'b24', M1M20: 'm1m20', M4M20: 'm4m20', REU615: 'reu615' }
|
|
68
|
+
},
|
|
69
|
+
'powermeter-satec': {
|
|
70
|
+
pkg: '@tetherto/mdk-worker-satec',
|
|
71
|
+
factory: 'startSatecWorker',
|
|
72
|
+
prefix: 'satec',
|
|
73
|
+
models: { PM180: 'pm180' }
|
|
74
|
+
},
|
|
75
|
+
'powermeter-schneider': {
|
|
76
|
+
pkg: '@tetherto/mdk-worker-schneider',
|
|
77
|
+
factory: 'startSchneiderWorker',
|
|
78
|
+
prefix: 'schneider',
|
|
79
|
+
models: { P3U30: 'p3u30', PM5340: 'pm5340' }
|
|
80
|
+
},
|
|
81
|
+
'sensor-seneca': {
|
|
82
|
+
pkg: '@tetherto/mdk-worker-seneca',
|
|
83
|
+
factory: 'startSenecaWorker',
|
|
84
|
+
prefix: 'seneca',
|
|
85
|
+
models: { 'Z-4RTD-2': 'z-4rtd-2' },
|
|
86
|
+
noModelOpt: true
|
|
87
|
+
},
|
|
88
|
+
'minerpool-ocean': {
|
|
89
|
+
pkg: '@tetherto/mdk-worker-ocean',
|
|
90
|
+
factory: 'startOceanPoolWorker',
|
|
91
|
+
prefix: 'ocean',
|
|
92
|
+
pool: true
|
|
93
|
+
},
|
|
94
|
+
'minerpool-f2pool': {
|
|
95
|
+
pkg: '@tetherto/mdk-worker-f2pool',
|
|
96
|
+
factory: 'startF2poolWorker',
|
|
97
|
+
prefix: 'f2pool',
|
|
98
|
+
pool: true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// `load` is the module loader (require by default; tests inject a fake).
|
|
103
|
+
function resolveWorkerBoot (worker, load = require) {
|
|
104
|
+
const spec = Object.hasOwn(WORKER_BOOTS, worker) ? WORKER_BOOTS[worker] : null
|
|
105
|
+
if (!spec) {
|
|
106
|
+
throw new Error(`ERR_MDK_WORKER_UNKNOWN: no runtime boot for "${worker}"`)
|
|
107
|
+
}
|
|
108
|
+
const mod = load(spec.pkg)
|
|
109
|
+
const factory = mod[spec.factory]
|
|
110
|
+
if (!factory) {
|
|
111
|
+
throw new Error(`ERR_MDK_WORKER_EXPORT: ${spec.factory} not found in ${worker}`)
|
|
112
|
+
}
|
|
113
|
+
return { spec, factory }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function requireMdk () {
|
|
117
|
+
return require('@tetherto/mdk-core')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Same topic-file rendezvous as getKernel's standalone DHT mode (a worker may
|
|
121
|
+
// boot before the kernel and writes the topic file a later getKernel() picks
|
|
122
|
+
// up).
|
|
123
|
+
function resolveKernelTopic () {
|
|
124
|
+
const { DEFAULT_TOPIC_FILE } = requireMdk()
|
|
125
|
+
if (fs.existsSync(DEFAULT_TOPIC_FILE)) {
|
|
126
|
+
return fs.readFileSync(DEFAULT_TOPIC_FILE, 'utf8').trim()
|
|
127
|
+
}
|
|
128
|
+
const kernelTopic = crypto.randomBytes(32).toString('hex')
|
|
129
|
+
fs.mkdirSync(path.dirname(DEFAULT_TOPIC_FILE), { recursive: true })
|
|
130
|
+
fs.writeFileSync(DEFAULT_TOPIC_FILE, kernelTopic, 'utf8')
|
|
131
|
+
return kernelTopic
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Worker RPC is fail-closed. A standalone worker admits the keys published to
|
|
135
|
+
// the default trusted-keys dir (the Kernel's client key, a Gateway's) plus any
|
|
136
|
+
// MDK_WORKER_ALLOWED_KEYS (comma-separated hex); MDK_WORKER_ALLOW_ALL=1 is the
|
|
137
|
+
// explicit development opt-out.
|
|
138
|
+
function resolveWorkerTrust (env = process.env) {
|
|
139
|
+
const { DEFAULT_TRUSTED_KEYS_DIR } = requireMdk()
|
|
140
|
+
const { readTrustedKeys } = require('@tetherto/mdk-core/lib/local-discovery')
|
|
141
|
+
const allowAll = env.MDK_WORKER_ALLOW_ALL === '1' || env.MDK_WORKER_ALLOW_ALL === 'true'
|
|
142
|
+
const allowedKeys = [
|
|
143
|
+
...(env.MDK_WORKER_ALLOWED_KEYS || '').split(',').map((k) => k.trim()).filter(Boolean),
|
|
144
|
+
...readTrustedKeys(DEFAULT_TRUSTED_KEYS_DIR).map((k) => k.key)
|
|
145
|
+
]
|
|
146
|
+
if (!allowAll && allowedKeys.length === 0) {
|
|
147
|
+
throw new Error(`ERR_MDK_WORKER_ALLOWED_KEYS_REQUIRED: no trusted keys in ${DEFAULT_TRUSTED_KEYS_DIR} — start the Kernel first (it publishes kernel.pub), set MDK_WORKER_ALLOWED_KEYS, or MDK_WORKER_ALLOW_ALL=1 for local development`)
|
|
148
|
+
}
|
|
149
|
+
return { allowedKeys: [...new Set(allowedKeys)], allowAll, dir: DEFAULT_TRUSTED_KEYS_DIR }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Keep following the trusted-keys dir after boot (a Gateway may publish later).
|
|
153
|
+
function _followTrust (handle, dir) {
|
|
154
|
+
const { trustKeysFromDir } = require('@tetherto/mdk-core/lib/local-discovery')
|
|
155
|
+
if (handle && handle.runtime) trustKeysFromDir(handle.runtime, dir)
|
|
156
|
+
return handle
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function runWorker (repoRoot) {
|
|
160
|
+
if (typeof repoRoot !== 'string' || !repoRoot) {
|
|
161
|
+
throw new Error('ERR_MDK_REPO_ROOT_REQUIRED: runWorker needs the MDK source checkout (MDK_REPO_ROOT)')
|
|
162
|
+
}
|
|
163
|
+
const { initialize } = requireMdk()
|
|
164
|
+
const worker = process.env.WORKER
|
|
165
|
+
const type = process.env.TYPE
|
|
166
|
+
const rack = process.env.RACK
|
|
167
|
+
|
|
168
|
+
if (!worker || !rack) {
|
|
169
|
+
throw new Error('ERR_MDK_WORKER_ENV: WORKER and RACK are required')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
initialize({ sources: repoRoot })
|
|
173
|
+
|
|
174
|
+
const root = process.env.MDK_WORKER_ROOT ||
|
|
175
|
+
path.join(process.cwd(), 'data', rack)
|
|
176
|
+
const kernelTopic = resolveKernelTopic()
|
|
177
|
+
const { allowedKeys, allowAll, dir: trustDir } = resolveWorkerTrust()
|
|
178
|
+
|
|
179
|
+
const { spec, factory } = resolveWorkerBoot(worker)
|
|
180
|
+
|
|
181
|
+
if (spec.pool) {
|
|
182
|
+
const workerId = `${spec.prefix}-${rack}`
|
|
183
|
+
const storeDir = path.join(root, 'workers', workerId, 'store')
|
|
184
|
+
fs.mkdirSync(storeDir, { recursive: true })
|
|
185
|
+
return _followTrust(await factory({ workerId, rack, storeDir, root, kernelTopic, allowedKeys, allowAll }), trustDir)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!type) {
|
|
189
|
+
throw new Error('ERR_MDK_WORKER_ENV: TYPE is required')
|
|
190
|
+
}
|
|
191
|
+
const model = spec.models[type]
|
|
192
|
+
if (!model) {
|
|
193
|
+
throw new Error(`ERR_MDK_WORKER_UNKNOWN: no plugin model for ${worker}:${type}`)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const workerId = `${spec.prefix}-${model}-${rack}`
|
|
197
|
+
const storeDir = path.join(root, 'workers', workerId, 'store')
|
|
198
|
+
fs.mkdirSync(storeDir, { recursive: true })
|
|
199
|
+
|
|
200
|
+
const opts = { workerId, storeDir, kernelTopic, allowedKeys, allowAll }
|
|
201
|
+
if (!spec.noModelOpt) opts.model = model
|
|
202
|
+
return _followTrust(await factory(opts), trustDir)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function runGateway () {
|
|
206
|
+
const gatewayRoot = gatewayPackageDir()
|
|
207
|
+
ensureConfigFromExamples(path.join(gatewayRoot, 'config'))
|
|
208
|
+
|
|
209
|
+
const env = process.env.MDK_ENV || 'development'
|
|
210
|
+
const port = process.env.PORT || '3000'
|
|
211
|
+
const workerPath = path.join(gatewayRoot, 'worker.js')
|
|
212
|
+
|
|
213
|
+
// Spawn so bfx-svc-boot-js uses gateway as serviceRoot (require.main.filename).
|
|
214
|
+
const child = spawn(process.execPath, [
|
|
215
|
+
workerPath,
|
|
216
|
+
'--wtype', 'wrk-node-http',
|
|
217
|
+
'--env', env,
|
|
218
|
+
'--port', String(port)
|
|
219
|
+
], {
|
|
220
|
+
cwd: gatewayRoot,
|
|
221
|
+
stdio: 'inherit',
|
|
222
|
+
env: process.env
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
return new Promise((resolve, reject) => {
|
|
226
|
+
child.on('error', reject)
|
|
227
|
+
child.on('exit', (code, signal) => {
|
|
228
|
+
if (code === 0) return resolve()
|
|
229
|
+
reject(new Error(
|
|
230
|
+
`gateway exited with code ${code}${signal ? ` (${signal})` : ''}`
|
|
231
|
+
))
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function main () {
|
|
237
|
+
const service = process.env.SERVICE
|
|
238
|
+
|
|
239
|
+
if (!service) {
|
|
240
|
+
throw new Error('ERR_MDK_SERVICE_ENV: SERVICE env var is required (gateway | worker)')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (service === 'gateway') {
|
|
244
|
+
return runGateway()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (service === 'worker') {
|
|
248
|
+
return runWorker(resolveRepoRoot())
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
throw new Error(`ERR_MDK_SERVICE_UNKNOWN: "${service}"`)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
resolveRepoRoot,
|
|
256
|
+
gatewayPackageDir,
|
|
257
|
+
ensureConfigFromExamples,
|
|
258
|
+
WORKER_BOOTS,
|
|
259
|
+
resolveWorkerBoot,
|
|
260
|
+
runGateway,
|
|
261
|
+
runWorker,
|
|
262
|
+
resolveWorkerTrust,
|
|
263
|
+
main
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (require.main === module) {
|
|
267
|
+
main().catch((err) => {
|
|
268
|
+
console.error(err)
|
|
269
|
+
process.exit(1)
|
|
270
|
+
})
|
|
271
|
+
}
|