@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.
@@ -1,16 +1,16 @@
1
- 'use strict'
2
-
3
- const { platform, tmpdir } = require('node:os')
4
- const { join } = require('node:path')
5
- const { createDirectory, safeRemove } = require('@platformatic/foundation')
6
-
7
- const fastify = require('fastify')
8
- const ws = require('ws')
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(require('@fastify/accepts'))
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 services')
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 services')
40
+ app.log.debug('restart applications')
41
41
  await runtime.restart()
42
42
  })
43
43
 
44
- app.get('/services', async () => {
45
- return runtime.getServices()
44
+ app.get('/applications', async () => {
45
+ return runtime.getApplications()
46
46
  })
47
47
 
48
- app.get('/services/:id', async request => {
48
+ app.get('/applications/:id', async request => {
49
49
  const { id } = request.params
50
- app.log.debug('get service details', { id })
51
- return runtime.getServiceDetails(id)
50
+ app.log.debug('get application details', { id })
51
+ return runtime.getApplicationDetails(id)
52
52
  })
53
53
 
54
- app.get('/services/:id/config', async request => {
54
+ app.get('/applications/:id/config', async request => {
55
55
  const { id } = request.params
56
- app.log.debug('get service config', { id })
57
- return runtime.getServiceConfig(id)
56
+ app.log.debug('get application config', { id })
57
+ return runtime.getApplicationConfig(id)
58
58
  })
59
59
 
60
- app.get('/services/:id/env', async request => {
60
+ app.get('/applications/:id/env', async request => {
61
61
  const { id } = request.params
62
- app.log.debug('get service config', { id })
63
- return runtime.getServiceEnv(id)
62
+ app.log.debug('get application config', { id })
63
+ return runtime.getApplicationEnv(id)
64
64
  })
65
65
 
66
- app.get('/services/:id/openapi-schema', async request => {
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.getServiceOpenapiSchema(id)
69
+ return runtime.getApplicationOpenapiSchema(id)
70
70
  })
71
71
 
72
- app.get('/services/:id/graphql-schema', async request => {
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.getServiceGraphqlSchema(id)
75
+ return runtime.getApplicationGraphqlSchema(id)
76
76
  })
77
77
 
78
- app.post('/services/:id/start', async request => {
78
+ app.post('/applications/:id/start', async request => {
79
79
  const { id } = request.params
80
- app.log.debug('start service', { id })
81
- await runtime.startService(id)
80
+ app.log.debug('start application', { id })
81
+ await runtime.startApplication(id)
82
82
  })
83
83
 
84
- app.post('/services/:id/stop', async request => {
84
+ app.post('/applications/:id/stop', async request => {
85
85
  const { id } = request.params
86
- app.log.debug('stop service', { id })
87
- await runtime.stopService(id)
86
+ app.log.debug('stop application', { id })
87
+ await runtime.stopApplication(id)
88
88
  })
89
89
 
90
- app.all('/services/:id/proxy/*', async (request, reply) => {
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(ws.createWebSocketStream(socket))
169
+ runtime.addLoggerDestination(createWebSocketStream(socket))
153
170
  })
154
171
  }
155
172
 
156
- async function startManagementApi (runtime, root) {
173
+ export async function startManagementApi (runtime) {
157
174
  const runtimePID = process.pid
158
175
 
159
- try {
160
- const runtimePIDDir = join(PLATFORMATIC_TMP_DIR, runtimePID.toString())
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 createDirectory(runtimePIDDir, true)
194
+ await safeRemove(runtimePIDDir)
163
195
  }
196
+ })
164
197
 
165
- let socketPath = null
166
- if (platform() === 'win32') {
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
- const managementApi = fastify()
173
- managementApi.register(require('@fastify/websocket'))
174
- managementApi.register(managementApiPlugin, { runtime, prefix: '/api/v1' })
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
- managementApi.addHook('onClose', async () => {
177
- if (platform() !== 'win32') {
178
- await safeRemove(runtimePIDDir)
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
- await managementApi.listen({ path: socketPath })
186
- return managementApi
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
- module.exports = { startManagementApi, managementApiPlugin }
221
+ return managementApi
222
+ }
@@ -1,6 +1,6 @@
1
- 'use strict'
2
-
3
- const fastify = require('fastify')
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(require('@fastify/accepts'))
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(require('@fastify/basic-auth'), {
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
- }