@platformatic/watt-extra 0.1.8 → 1.0.0
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/.github/workflows/test.yml +1 -1
- package/lib/watt.js +7 -6
- package/package.json +8 -8
- package/plugins/compliancy.js +1 -1
- package/test/auto-cache.test.js +1 -1
- package/test/helper.js +6 -4
- package/test/init.test.js +3 -3
- package/test/metrics.test.js +107 -0
- package/test/patch-config.test.js +1 -0
|
@@ -41,7 +41,7 @@ jobs:
|
|
|
41
41
|
version: 10
|
|
42
42
|
|
|
43
43
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
44
|
-
uses: actions/setup-node@
|
|
44
|
+
uses: actions/setup-node@v5
|
|
45
45
|
with:
|
|
46
46
|
node-version: ${{ matrix.node-version }}
|
|
47
47
|
# https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#use-private-packages
|
package/lib/watt.js
CHANGED
|
@@ -201,6 +201,7 @@ class Watt {
|
|
|
201
201
|
applicationId: this.#instanceConfig?.applicationId,
|
|
202
202
|
instanceId: this.#instanceId,
|
|
203
203
|
},
|
|
204
|
+
applicationLabel: this.#instanceConfig?.applicationMetricsLabel ?? 'serviceId'
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
if (this.#env.PLT_DISABLE_FLAMEGRAPHS !== true) {
|
|
@@ -251,11 +252,11 @@ class Watt {
|
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
#getTrafficInterceptorConfig () {
|
|
254
|
-
if (!this.#instanceConfig?.iccServices?.
|
|
255
|
+
if (!this.#instanceConfig?.iccServices?.trafficInspector?.url) {
|
|
255
256
|
return
|
|
256
257
|
}
|
|
257
|
-
const { origin:
|
|
258
|
-
this.#instanceConfig.iccServices.
|
|
258
|
+
const { origin: trafficInspectorOrigin, pathname: trafficInspectorPath } = new URL(
|
|
259
|
+
this.#instanceConfig.iccServices.trafficInspector.url
|
|
259
260
|
)
|
|
260
261
|
return {
|
|
261
262
|
module: require.resolve(
|
|
@@ -271,9 +272,9 @@ class Watt {
|
|
|
271
272
|
},
|
|
272
273
|
maxResponseSize: 5 * 1024 * 1024, // 5MB
|
|
273
274
|
trafficInspectorOptions: {
|
|
274
|
-
url:
|
|
275
|
-
pathSendBody: join(
|
|
276
|
-
pathSendMeta: join(
|
|
275
|
+
url: trafficInspectorOrigin,
|
|
276
|
+
pathSendBody: join(trafficInspectorPath, '/requests'),
|
|
277
|
+
pathSendMeta: join(trafficInspectorPath, '/requests/hash'),
|
|
277
278
|
},
|
|
278
279
|
matchingDomains: [this.#env.PLT_APP_INTERNAL_SUB_DOMAIN],
|
|
279
280
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/watt-extra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "The Platformatic runtime manager",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -13,25 +13,25 @@
|
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@fastify/websocket": "^11.1.0",
|
|
16
|
-
"@platformatic/composer": "^3.0
|
|
17
|
-
"@platformatic/next": "^3.0
|
|
18
|
-
"@platformatic/node": "^3.0
|
|
19
|
-
"@platformatic/service": "^3.0
|
|
16
|
+
"@platformatic/composer": "^3.3.0",
|
|
17
|
+
"@platformatic/next": "^3.3.0",
|
|
18
|
+
"@platformatic/node": "^3.3.0",
|
|
19
|
+
"@platformatic/service": "^3.3.0",
|
|
20
20
|
"borp": "^0.20.0",
|
|
21
21
|
"eslint": "9",
|
|
22
22
|
"fastify": "^5.4.0",
|
|
23
23
|
"fastify-plugin": "^5.0.1",
|
|
24
24
|
"neostandard": "^0.12.0",
|
|
25
25
|
"next": "^15.3.4",
|
|
26
|
-
"platformatic": "^3.0
|
|
26
|
+
"platformatic": "^3.3.0",
|
|
27
27
|
"pprof-format": "^2.1.0",
|
|
28
28
|
"why-is-node-running": "^2.3.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@datadog/pprof": "^5.9.0",
|
|
32
32
|
"@fastify/error": "^4.2.0",
|
|
33
|
-
"@platformatic/runtime": "^3.0
|
|
34
|
-
"@platformatic/wattpm-pprof-capture": "^3.0
|
|
33
|
+
"@platformatic/runtime": "^3.3.0",
|
|
34
|
+
"@platformatic/wattpm-pprof-capture": "^3.3.0",
|
|
35
35
|
"avvio": "^9.1.0",
|
|
36
36
|
"chalk": "^4.1.2",
|
|
37
37
|
"commist": "^3.2.0",
|
package/plugins/compliancy.js
CHANGED
package/test/auto-cache.test.js
CHANGED
|
@@ -128,7 +128,7 @@ test('should spawn an app with auto caching', async (t) => {
|
|
|
128
128
|
assert.strictEqual(headers['x-foo-bar'], undefined)
|
|
129
129
|
assert.strictEqual(headers['x-custom-cache-tags'], undefined)
|
|
130
130
|
|
|
131
|
-
// Wait for interceptor to send data to the
|
|
131
|
+
// Wait for interceptor to send data to the Traffic Inspector
|
|
132
132
|
await sleep(1000)
|
|
133
133
|
|
|
134
134
|
assert.strictEqual(savedRequestHashes.length, 1)
|
package/test/helper.js
CHANGED
|
@@ -34,6 +34,7 @@ async function startICC (t, opts = {}) {
|
|
|
34
34
|
let {
|
|
35
35
|
applicationId,
|
|
36
36
|
applicationName,
|
|
37
|
+
applicationMetricsLabel,
|
|
37
38
|
iccServices,
|
|
38
39
|
iccConfig = {},
|
|
39
40
|
enableOpenTelemetry = false,
|
|
@@ -46,8 +47,8 @@ async function startICC (t, opts = {}) {
|
|
|
46
47
|
riskEngine: {
|
|
47
48
|
url: 'http://127.0.0.1:3000/risk-service'
|
|
48
49
|
},
|
|
49
|
-
|
|
50
|
-
url: 'http://127.0.0.1:3000/
|
|
50
|
+
trafficInspector: {
|
|
51
|
+
url: 'http://127.0.0.1:3000/traffic-inspector'
|
|
51
52
|
},
|
|
52
53
|
compliance: {
|
|
53
54
|
url: 'http://127.0.0.1:3000/compliance'
|
|
@@ -108,6 +109,7 @@ async function startICC (t, opts = {}) {
|
|
|
108
109
|
return controlPlaneResponse || {
|
|
109
110
|
applicationId,
|
|
110
111
|
applicationName,
|
|
112
|
+
applicationMetricsLabel,
|
|
111
113
|
iccServices,
|
|
112
114
|
config: iccConfig,
|
|
113
115
|
enableOpenTelemetry,
|
|
@@ -143,7 +145,7 @@ async function startICC (t, opts = {}) {
|
|
|
143
145
|
})
|
|
144
146
|
}, { prefix: '/compliance' })
|
|
145
147
|
|
|
146
|
-
//
|
|
148
|
+
// Traffic Inspector
|
|
147
149
|
await icc.register(async (icc) => {
|
|
148
150
|
icc.post('/requests/hash', async (req) => {
|
|
149
151
|
const { taxonomyId, applicationId } = JSON.parse(req.headers['x-labels'])
|
|
@@ -159,7 +161,7 @@ async function startICC (t, opts = {}) {
|
|
|
159
161
|
|
|
160
162
|
opts.saveRequest?.({ taxonomyId, applicationId, request, response })
|
|
161
163
|
})
|
|
162
|
-
}, { prefix: '/
|
|
164
|
+
}, { prefix: '/traffic-inspector' })
|
|
163
165
|
|
|
164
166
|
// Risk Service
|
|
165
167
|
await icc.register(async (icc) => {
|
package/test/init.test.js
CHANGED
|
@@ -63,7 +63,7 @@ test('init plugin with optional PLT_APP_NAME - uses ICC response', async (t) =>
|
|
|
63
63
|
applicationName, // ICC returns the application name
|
|
64
64
|
iccServices: {
|
|
65
65
|
riskEngine: { url: 'http://127.0.0.1:3000/risk-service' },
|
|
66
|
-
|
|
66
|
+
trafficInspector: { url: 'http://127.0.0.1:3000/traffic-inspector' },
|
|
67
67
|
compliance: { url: 'http://127.0.0.1:3000/compliance' },
|
|
68
68
|
cron: { url: 'http://127.0.0.1:3000/cron' },
|
|
69
69
|
scaler: { url: 'http://127.0.0.1:3000/scaler' }
|
|
@@ -166,7 +166,7 @@ test('init plugin sends correct request structure when PLT_APP_NAME provided', a
|
|
|
166
166
|
applicationName,
|
|
167
167
|
iccServices: {
|
|
168
168
|
riskEngine: { url: 'http://127.0.0.1:3000/risk-service' },
|
|
169
|
-
|
|
169
|
+
trafficInspector: { url: 'http://127.0.0.1:3000/traffic-inspector' },
|
|
170
170
|
compliance: { url: 'http://127.0.0.1:3000/compliance' },
|
|
171
171
|
cron: { url: 'http://127.0.0.1:3000/cron' },
|
|
172
172
|
scaler: { url: 'http://127.0.0.1:3000/scaler' }
|
|
@@ -212,7 +212,7 @@ test('init plugin sends request without applicationName when not provided', asyn
|
|
|
212
212
|
applicationName, // ICC provides the name
|
|
213
213
|
iccServices: {
|
|
214
214
|
riskEngine: { url: 'http://127.0.0.1:3000/risk-service' },
|
|
215
|
-
|
|
215
|
+
trafficInspector: { url: 'http://127.0.0.1:3000/traffic-inspector' },
|
|
216
216
|
compliance: { url: 'http://127.0.0.1:3000/compliance' },
|
|
217
217
|
cron: { url: 'http://127.0.0.1:3000/cron' },
|
|
218
218
|
scaler: { url: 'http://127.0.0.1:3000/scaler' }
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import { randomUUID } from 'node:crypto'
|
|
4
|
+
import { request } from 'undici'
|
|
5
|
+
import { join, dirname } from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { setUpEnvironment, startICC } from './helper.js'
|
|
8
|
+
import { start } from '../index.js'
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
11
|
+
const __dirname = dirname(__filename)
|
|
12
|
+
|
|
13
|
+
test('should generate metrics with a correct labels', async (t) => {
|
|
14
|
+
const applicationName = 'test-app'
|
|
15
|
+
const applicationId = randomUUID()
|
|
16
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
17
|
+
|
|
18
|
+
const icc = await startICC(t, {
|
|
19
|
+
applicationId,
|
|
20
|
+
applicationName
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
process.env.PLT_TEST_APP_1_URL = 'http://test-app-1:3042'
|
|
24
|
+
t.after(() => {
|
|
25
|
+
delete process.env.PLT_TEST_APP_1_URL
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
setUpEnvironment({
|
|
29
|
+
PLT_APP_NAME: applicationName,
|
|
30
|
+
PLT_APP_DIR: applicationPath,
|
|
31
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const app = await start()
|
|
35
|
+
|
|
36
|
+
t.after(async () => {
|
|
37
|
+
await app.close()
|
|
38
|
+
await icc.close()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const { statusCode, body } = await request('http://127.0.0.1:9090/metrics', {
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json',
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
assert.strictEqual(statusCode, 200)
|
|
47
|
+
|
|
48
|
+
const metrics = await body.json()
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
const eluMetrics = metrics.find((metric) => metric.name === 'nodejs_eventloop_utilization')
|
|
52
|
+
assert.ok(eluMetrics)
|
|
53
|
+
|
|
54
|
+
const labels = eluMetrics.values[0].labels
|
|
55
|
+
assert.strictEqual(labels.applicationId, applicationId)
|
|
56
|
+
assert.strictEqual(labels.serviceId, 'main')
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('should generate metrics with a custom metrics label', async (t) => {
|
|
61
|
+
const applicationName = 'test-app'
|
|
62
|
+
const applicationId = randomUUID()
|
|
63
|
+
const applicationPath = join(__dirname, 'fixtures', 'service-1')
|
|
64
|
+
const applicationMetricsLabel = 'customLabel'
|
|
65
|
+
|
|
66
|
+
const icc = await startICC(t, {
|
|
67
|
+
applicationId,
|
|
68
|
+
applicationName,
|
|
69
|
+
applicationMetricsLabel
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
process.env.PLT_TEST_APP_1_URL = 'http://test-app-1:3042'
|
|
73
|
+
t.after(() => {
|
|
74
|
+
delete process.env.PLT_TEST_APP_1_URL
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
setUpEnvironment({
|
|
78
|
+
PLT_APP_NAME: applicationName,
|
|
79
|
+
PLT_APP_DIR: applicationPath,
|
|
80
|
+
PLT_ICC_URL: 'http://127.0.0.1:3000',
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const app = await start()
|
|
84
|
+
|
|
85
|
+
t.after(async () => {
|
|
86
|
+
await app.close()
|
|
87
|
+
await icc.close()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const { statusCode, body } = await request('http://127.0.0.1:9090/metrics', {
|
|
91
|
+
headers: {
|
|
92
|
+
accept: 'application/json',
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
assert.strictEqual(statusCode, 200)
|
|
96
|
+
|
|
97
|
+
const metrics = await body.json()
|
|
98
|
+
|
|
99
|
+
{
|
|
100
|
+
const eluMetrics = metrics.find((metric) => metric.name === 'nodejs_eventloop_utilization')
|
|
101
|
+
assert.ok(eluMetrics)
|
|
102
|
+
|
|
103
|
+
const labels = eluMetrics.values[0].labels
|
|
104
|
+
assert.strictEqual(labels.applicationId, applicationId)
|
|
105
|
+
assert.strictEqual(labels[applicationMetricsLabel], 'main')
|
|
106
|
+
}
|
|
107
|
+
})
|