@platformatic/runtime 3.0.0-alpha.5 → 3.0.0-alpha.8
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/config.d.ts +11 -7
- package/eslint.config.js +2 -4
- package/index.d.ts +11 -11
- package/index.js +35 -46
- package/lib/config.js +159 -102
- package/lib/errors.js +65 -99
- package/lib/generator.js +160 -164
- package/lib/logger.js +6 -8
- package/lib/management-api.js +91 -63
- package/lib/prom-server.js +10 -14
- package/lib/runtime.js +815 -747
- package/lib/scheduler.js +13 -15
- package/lib/schema.js +11 -8
- package/lib/shared-http-cache.js +5 -9
- package/lib/upgrade.js +5 -9
- package/lib/utils.js +6 -14
- package/lib/version.js +7 -0
- package/lib/versions/v1.36.0.js +2 -4
- package/lib/versions/v1.5.0.js +2 -4
- package/lib/versions/v2.0.0.js +3 -5
- package/lib/versions/v3.0.0.js +16 -0
- package/lib/worker/{app.js → controller.js} +68 -63
- package/lib/worker/http-cache.js +11 -14
- package/lib/worker/interceptors.js +14 -18
- package/lib/worker/itc.js +85 -79
- package/lib/worker/main.js +49 -55
- package/lib/worker/messaging.js +23 -27
- package/lib/worker/round-robin-map.js +23 -19
- package/lib/worker/shared-context.js +2 -6
- package/lib/worker/symbols.js +12 -29
- package/package.json +24 -23
- package/schema.json +281 -20
- package/lib/dependencies.js +0 -65
package/lib/management-api.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import fastifyAccepts from '@fastify/accepts'
|
|
2
|
+
import fastifyWebsocket from '@fastify/websocket'
|
|
3
|
+
import { createDirectory, safeRemove } from '@platformatic/foundation'
|
|
4
|
+
import fastify from 'fastify'
|
|
5
|
+
import { platform, tmpdir } from 'node:os'
|
|
6
|
+
import { join } from 'node:path'
|
|
7
|
+
import { setTimeout as sleep } from 'node:timers/promises'
|
|
8
|
+
import { createWebSocketStream } from 'ws'
|
|
9
9
|
|
|
10
10
|
const PLATFORMATIC_TMP_DIR = join(tmpdir(), 'platformatic', 'runtimes')
|
|
11
11
|
|
|
12
|
-
async function managementApiPlugin (app, opts) {
|
|
13
|
-
app.register(
|
|
12
|
+
export async function managementApiPlugin (app, opts) {
|
|
13
|
+
app.register(fastifyAccepts)
|
|
14
14
|
|
|
15
15
|
const runtime = opts.runtime
|
|
16
16
|
|
|
@@ -32,62 +32,62 @@ async function managementApiPlugin (app, opts) {
|
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
app.post('/stop', async () => {
|
|
35
|
-
app.log.debug('stop
|
|
35
|
+
app.log.debug('stop applications')
|
|
36
36
|
await runtime.close()
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
app.post('/restart', async () => {
|
|
40
|
-
app.log.debug('restart
|
|
40
|
+
app.log.debug('restart applications')
|
|
41
41
|
await runtime.restart()
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
app.get('/
|
|
45
|
-
return runtime.
|
|
44
|
+
app.get('/applications', async () => {
|
|
45
|
+
return runtime.getApplications()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
app.get('/
|
|
48
|
+
app.get('/applications/:id', async request => {
|
|
49
49
|
const { id } = request.params
|
|
50
|
-
app.log.debug('get
|
|
51
|
-
return runtime.
|
|
50
|
+
app.log.debug('get application details', { id })
|
|
51
|
+
return runtime.getApplicationDetails(id)
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
app.get('/
|
|
54
|
+
app.get('/applications/:id/config', async request => {
|
|
55
55
|
const { id } = request.params
|
|
56
|
-
app.log.debug('get
|
|
57
|
-
return runtime.
|
|
56
|
+
app.log.debug('get application config', { id })
|
|
57
|
+
return runtime.getApplicationConfig(id)
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
app.get('/
|
|
60
|
+
app.get('/applications/:id/env', async request => {
|
|
61
61
|
const { id } = request.params
|
|
62
|
-
app.log.debug('get
|
|
63
|
-
return runtime.
|
|
62
|
+
app.log.debug('get application config', { id })
|
|
63
|
+
return runtime.getApplicationEnv(id)
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
app.get('/
|
|
66
|
+
app.get('/applications/:id/openapi-schema', async request => {
|
|
67
67
|
const { id } = request.params
|
|
68
68
|
app.log.debug('get openapi-schema', { id })
|
|
69
|
-
return runtime.
|
|
69
|
+
return runtime.getApplicationOpenapiSchema(id)
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
app.get('/
|
|
72
|
+
app.get('/applications/:id/graphql-schema', async request => {
|
|
73
73
|
const { id } = request.params
|
|
74
74
|
app.log.debug('get graphql-schema', { id })
|
|
75
|
-
return runtime.
|
|
75
|
+
return runtime.getApplicationGraphqlSchema(id)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
app.post('/
|
|
78
|
+
app.post('/applications/:id/start', async request => {
|
|
79
79
|
const { id } = request.params
|
|
80
|
-
app.log.debug('start
|
|
81
|
-
await runtime.
|
|
80
|
+
app.log.debug('start application', { id })
|
|
81
|
+
await runtime.startApplication(id)
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
app.post('/
|
|
84
|
+
app.post('/applications/:id/stop', async request => {
|
|
85
85
|
const { id } = request.params
|
|
86
|
-
app.log.debug('stop
|
|
87
|
-
await runtime.
|
|
86
|
+
app.log.debug('stop application', { id })
|
|
87
|
+
await runtime.stopApplication(id)
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
app.all('/
|
|
90
|
+
app.all('/applications/:id/proxy/*', async (request, reply) => {
|
|
91
91
|
const { id, '*': requestUrl } = request.params
|
|
92
92
|
app.log.debug('proxy request', { id, requestUrl })
|
|
93
93
|
|
|
@@ -112,6 +112,23 @@ async function managementApiPlugin (app, opts) {
|
|
|
112
112
|
reply.code(res.statusCode).headers(res.headers).send(res.body)
|
|
113
113
|
})
|
|
114
114
|
|
|
115
|
+
app.post('/applications/:id/pprof/start', async (request, reply) => {
|
|
116
|
+
const { id } = request.params
|
|
117
|
+
app.log.debug('start profiling', { id })
|
|
118
|
+
|
|
119
|
+
const options = request.body || {}
|
|
120
|
+
await runtime.startApplicationProfiling(id, options)
|
|
121
|
+
reply.code(200).send({})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
app.post('/applications/:id/pprof/stop', async (request, reply) => {
|
|
125
|
+
const { id } = request.params
|
|
126
|
+
app.log.debug('stop profiling', { id })
|
|
127
|
+
|
|
128
|
+
const profileData = await runtime.stopApplicationProfiling(id)
|
|
129
|
+
reply.type('application/octet-stream').code(200).send(profileData)
|
|
130
|
+
})
|
|
131
|
+
|
|
115
132
|
app.get('/metrics', { logLevel: 'debug' }, async (req, reply) => {
|
|
116
133
|
const accepts = req.accepts()
|
|
117
134
|
|
|
@@ -149,46 +166,57 @@ async function managementApiPlugin (app, opts) {
|
|
|
149
166
|
})
|
|
150
167
|
|
|
151
168
|
app.get('/logs/live', { websocket: true }, async (socket, req) => {
|
|
152
|
-
runtime.addLoggerDestination(
|
|
169
|
+
runtime.addLoggerDestination(createWebSocketStream(socket))
|
|
153
170
|
})
|
|
154
171
|
}
|
|
155
172
|
|
|
156
|
-
async function startManagementApi (runtime
|
|
173
|
+
export async function startManagementApi (runtime) {
|
|
157
174
|
const runtimePID = process.pid
|
|
158
175
|
|
|
159
|
-
|
|
160
|
-
|
|
176
|
+
const runtimePIDDir = join(PLATFORMATIC_TMP_DIR, runtimePID.toString())
|
|
177
|
+
if (platform() !== 'win32') {
|
|
178
|
+
await createDirectory(runtimePIDDir, true)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let socketPath = null
|
|
182
|
+
if (platform() === 'win32') {
|
|
183
|
+
socketPath = '\\\\.\\pipe\\platformatic-' + runtimePID.toString()
|
|
184
|
+
} else {
|
|
185
|
+
socketPath = join(runtimePIDDir, 'socket')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const managementApi = fastify()
|
|
189
|
+
managementApi.register(fastifyWebsocket)
|
|
190
|
+
managementApi.register(managementApiPlugin, { runtime, prefix: '/api/v1' })
|
|
191
|
+
|
|
192
|
+
managementApi.addHook('onClose', async () => {
|
|
161
193
|
if (platform() !== 'win32') {
|
|
162
|
-
await
|
|
194
|
+
await safeRemove(runtimePIDDir)
|
|
163
195
|
}
|
|
196
|
+
})
|
|
164
197
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
socketPath = '\\\\.\\pipe\\platformatic-' + runtimePID.toString()
|
|
168
|
-
} else {
|
|
169
|
-
socketPath = join(runtimePIDDir, 'socket')
|
|
170
|
-
}
|
|
198
|
+
// When the runtime closes, close the management API as well
|
|
199
|
+
runtime.on('closed', managementApi.close.bind(managementApi))
|
|
171
200
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
201
|
+
/*
|
|
202
|
+
If runtime are started multiple times in a short
|
|
203
|
+
period of time (like in tests), there is a chance that the pipe is still in use
|
|
204
|
+
as the manament API server is closed after the runtime is closed (see event handler above).
|
|
175
205
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
206
|
+
Since it's a very rare case, we simply retry couple of times.
|
|
207
|
+
*/
|
|
208
|
+
for (let i = 0; i < 5; i++) {
|
|
209
|
+
try {
|
|
210
|
+
await managementApi.listen({ path: socketPath })
|
|
211
|
+
break
|
|
212
|
+
} catch (e) {
|
|
213
|
+
if (i === 5) {
|
|
214
|
+
throw e
|
|
179
215
|
}
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
// When the runtime closes, close the management API as well
|
|
183
|
-
runtime.on('closed', managementApi.close.bind(managementApi))
|
|
184
216
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
/* c8 ignore next 4 */
|
|
188
|
-
} catch (err) {
|
|
189
|
-
console.error(err)
|
|
190
|
-
process.exit(1)
|
|
217
|
+
await sleep(100)
|
|
218
|
+
}
|
|
191
219
|
}
|
|
192
|
-
}
|
|
193
220
|
|
|
194
|
-
|
|
221
|
+
return managementApi
|
|
222
|
+
}
|
package/lib/prom-server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import fastifyAccepts from '@fastify/accepts'
|
|
2
|
+
import fastifyBasicAuth from '@fastify/basic-auth'
|
|
3
|
+
import fastify from 'fastify'
|
|
4
4
|
|
|
5
5
|
const DEFAULT_HOSTNAME = '0.0.0.0'
|
|
6
6
|
const DEFAULT_PORT = 9090
|
|
@@ -67,7 +67,7 @@ async function checkLiveness (runtime) {
|
|
|
67
67
|
return { response, status }
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
async function startPrometheusServer (runtime, opts) {
|
|
70
|
+
export async function startPrometheusServer (runtime, opts) {
|
|
71
71
|
if (opts.enabled === false) {
|
|
72
72
|
return
|
|
73
73
|
}
|
|
@@ -77,19 +77,19 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
77
77
|
const auth = opts.auth ?? null
|
|
78
78
|
|
|
79
79
|
const promServer = fastify({ name: 'Prometheus server' })
|
|
80
|
-
promServer.register(
|
|
80
|
+
promServer.register(fastifyAccepts)
|
|
81
81
|
|
|
82
82
|
let onRequestHook
|
|
83
83
|
if (auth) {
|
|
84
84
|
const { username, password } = auth
|
|
85
85
|
|
|
86
|
-
await promServer.register(
|
|
86
|
+
await promServer.register(fastifyBasicAuth, {
|
|
87
87
|
validate: function (user, pass, req, reply, done) {
|
|
88
88
|
if (username !== user || password !== pass) {
|
|
89
89
|
return reply.code(401).send({ message: 'Unauthorized' })
|
|
90
90
|
}
|
|
91
91
|
return done()
|
|
92
|
-
}
|
|
92
|
+
}
|
|
93
93
|
})
|
|
94
94
|
onRequestHook = promServer.basicAuth
|
|
95
95
|
}
|
|
@@ -129,7 +129,7 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
129
129
|
reply.type('text/plain')
|
|
130
130
|
}
|
|
131
131
|
return (await runtime.getMetrics(reqType)).metrics
|
|
132
|
-
}
|
|
132
|
+
}
|
|
133
133
|
})
|
|
134
134
|
|
|
135
135
|
if (opts.readiness !== false) {
|
|
@@ -167,7 +167,7 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
167
167
|
reply.status(failStatusCode).send(failBody)
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
}
|
|
170
|
+
}
|
|
171
171
|
})
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -206,14 +206,10 @@ async function startPrometheusServer (runtime, opts) {
|
|
|
206
206
|
reply.status(failStatusCode).send(readiness?.body || failBody)
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
}
|
|
209
|
+
}
|
|
210
210
|
})
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
await promServer.listen({ port, host })
|
|
214
214
|
return promServer
|
|
215
215
|
}
|
|
216
|
-
|
|
217
|
-
module.exports = {
|
|
218
|
-
startPrometheusServer,
|
|
219
|
-
}
|