@platformatic/watt-extra 1.12.0 → 1.13.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/watt-extra",
3
- "version": "1.12.0",
3
+ "version": "1.13.0-alpha.0",
4
4
  "description": "The Platformatic runtime manager",
5
5
  "type": "module",
6
6
  "scripts": {
package/plugins/alerts.js CHANGED
@@ -130,7 +130,7 @@ async function alerts (app, _opts) {
130
130
  lastServicesAlertTime[serviceId] = currentTime
131
131
  delete healthInfo.healthConfig
132
132
 
133
- const authHeaders = await app.getAuthorizationHeader()
133
+ const authHeaders = await app.getAuthorizationHeaders()
134
134
 
135
135
  const { statusCode, body } = await request(`${scalerUrl}/alerts`, {
136
136
  method: 'POST',
package/plugins/auth.js CHANGED
@@ -3,6 +3,8 @@ import { Agent } from 'undici'
3
3
 
4
4
  const K8S_TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token'
5
5
 
6
+ // ── Helpers ────────────────────────────────────────────────────────────────
7
+
6
8
  function decodeJwtPayload (token) {
7
9
  try {
8
10
  if (!token) return null
@@ -10,7 +12,7 @@ function decodeJwtPayload (token) {
10
12
  if (!base64Payload) return null
11
13
  const payload = Buffer.from(base64Payload, 'base64').toString('utf8')
12
14
  return JSON.parse(payload)
13
- } catch (err) {
15
+ } catch {
14
16
  return null
15
17
  }
16
18
  }
@@ -18,72 +20,115 @@ function decodeJwtPayload (token) {
18
20
  function isTokenExpired (token, offset = 0) {
19
21
  const payload = decodeJwtPayload(token)
20
22
  if (!payload || !payload.exp) return true
23
+ return payload.exp <= Math.floor(Date.now() / 1000) + offset
24
+ }
21
25
 
22
- // Check if token is expired
23
- const currentTime = Math.floor(Date.now() / 1000)
24
- return payload.exp <= (currentTime + offset)
26
+ function detectProvider () {
27
+ if (process.env.ECS_CONTAINER_METADATA_URI_V4) return 'ecs'
28
+ if (process.env.KUBERNETES_SERVICE_HOST) return 'k8s'
29
+ return 'k8s' // safe default
25
30
  }
26
31
 
27
- async function authPlugin (app) {
28
- // Add a 1 min offset to update the token before it expires
29
- // via runtime shared context
30
- const offset = parseInt(process.env.PLT_JWT_EXPIRATION_OFFSET_SEC ?? 0)
32
+ async function loadK8sToken (log) {
33
+ let token
34
+ try {
35
+ await stat(K8S_TOKEN_PATH)
36
+ log.info('Loading JWT token from K8s service account')
37
+ token = await readFile(K8S_TOKEN_PATH, 'utf8')
38
+ } catch {
39
+ log.warn('Failed to load JWT token from K8s service account')
40
+ }
41
+ if (!token) {
42
+ log.warn('K8s token not found, falling back to environment variable')
43
+ token = process.env.PLT_TEST_TOKEN
44
+ }
45
+ return token
46
+ }
31
47
 
32
- async function loadToken () {
33
- let token
34
- try {
35
- await stat(K8S_TOKEN_PATH)
36
- app.log.info('Loading JWT token from K8s service account')
37
- token = await readFile(K8S_TOKEN_PATH, 'utf8')
38
- } catch (err) {
39
- app.log.warn('Failed to load JWT token from K8s service account')
40
- }
48
+ // Read ECS task identity (TaskARN suffix + cluster) from the task metadata
49
+ // endpoint. K8s identity travels via the SA JWT, so no resolution needed there.
50
+ async function resolveEcsIdentity (log) {
51
+ const metadataUrl = `${process.env.ECS_CONTAINER_METADATA_URI_V4}/task`
52
+ try {
53
+ const res = await fetch(metadataUrl)
54
+ if (!res.ok) throw new Error(`status ${res.status}`)
55
+ const meta = await res.json()
56
+ const id = meta.TaskARN?.split('/').pop()
57
+ const namespace = meta.Cluster
58
+ if (!id || !namespace) throw new Error('TaskARN or Cluster missing in metadata')
59
+ log.info({ id, namespace }, 'Resolved ECS task identity')
60
+ return { id, namespace }
61
+ } catch (err) {
62
+ log.error({ err, metadataUrl }, 'Failed to read ECS task metadata')
63
+ return null
64
+ }
65
+ }
41
66
 
42
- if (!token) {
43
- app.log.warn('K8s token not found, falling back to environment variable')
44
- token = process.env.PLT_TEST_TOKEN
45
- }
67
+ // ── Provider strategies ────────────────────────────────────────────────────
46
68
 
47
- return token
48
- }
69
+ // K8s strategy: load the SA JWT once, refresh on expiry, send as Bearer.
70
+ async function createK8sStrategy (app, offset) {
71
+ app.token = await loadK8sToken(app.log)
49
72
 
50
- const getAuthorizationHeader = async (headers = {}) => {
73
+ return async function getHeaders () {
51
74
  if (app.token && isTokenExpired(app.token, offset)) {
52
75
  app.log.info('JWT token expired, reloading')
53
- app.token = await loadToken()
76
+ app.token = await loadK8sToken(app.log)
54
77
 
55
78
  app.watt?.updateSharedContext({
56
79
  iccAuthHeaders: { authorization: `Bearer ${app.token}` }
57
- }).catch((err) => {
80
+ }).catch(err => {
58
81
  app.log.error({ err }, 'Failed to update jwt token in shared context')
59
82
  })
60
83
  }
84
+ return { authorization: `Bearer ${app.token}` }
85
+ }
86
+ }
61
87
 
88
+ // ECS strategy: resolve task identity once and send as explicit headers.
89
+ // Unauthenticated for now; future hardening will replace this with a Sigv4-
90
+ // presigned sts:GetCallerIdentity proof carried in an Authorization header.
91
+ async function createEcsStrategy (app) {
92
+ app.machineIdentity = await resolveEcsIdentity(app.log)
93
+
94
+ return async function getHeaders () {
95
+ if (!app.machineIdentity) return {}
62
96
  return {
63
- ...headers,
64
- authorization: `Bearer ${app.token}`
97
+ 'x-ecs-task-id': app.machineIdentity.id,
98
+ 'x-ecs-cluster': app.machineIdentity.namespace
65
99
  }
66
100
  }
101
+ }
102
+
103
+ // ── Plugin ─────────────────────────────────────────────────────────────────
104
+
105
+ async function authPlugin (app) {
106
+ // 1 min offset to refresh the token before it actually expires.
107
+ const offset = parseInt(process.env.PLT_JWT_EXPIRATION_OFFSET_SEC ?? 0)
108
+ const provider = detectProvider()
109
+
110
+ const getProviderHeaders = provider === 'ecs'
111
+ ? await createEcsStrategy(app)
112
+ : await createK8sStrategy(app, offset)
113
+
114
+ async function getAuthorizationHeaders (headers = {}) {
115
+ return { ...headers, ...(await getProviderHeaders()) }
116
+ }
67
117
 
68
- const authorizationTokenInterceptor = dispatch => {
118
+ function authorizationTokenInterceptor (dispatch) {
69
119
  return async function InterceptedDispatch (opts, handler) {
70
- opts.headers = await getAuthorizationHeader(opts.headers)
120
+ opts.headers = await getAuthorizationHeaders(opts.headers)
71
121
  return dispatch(opts, handler)
72
122
  }
73
123
  }
74
124
 
75
- app.token = await loadToken()
76
-
77
- setInterval(async () => {
78
- // Check if token is expired to propagate it to the runtime
79
- // via the shared context
80
- await getAuthorizationHeader()
81
- }, offset ? offset * 1000 / 2 : 30000).unref()
125
+ // Periodically call the strategy so K8s token refresh propagates to the
126
+ // runtime shared context before requests need it. No-op for ECS.
127
+ setInterval(getAuthorizationHeaders, offset ? offset * 1000 / 2 : 30000).unref()
82
128
 
83
- // We cannot change the global dispatcher because it's shared with the runtime main thread.
84
- const wattDispatcher = new Agent()
85
- app.dispatcher = wattDispatcher.compose(authorizationTokenInterceptor)
86
- app.getAuthorizationHeader = getAuthorizationHeader
129
+ // Can't replace the global dispatcher (shared with the runtime main thread).
130
+ app.dispatcher = new Agent().compose(authorizationTokenInterceptor)
131
+ app.getAuthorizationHeaders = getAuthorizationHeaders
87
132
  }
88
133
 
89
134
  export default authPlugin
@@ -30,7 +30,7 @@ async function compliancy (app, _opts) {
30
30
  // There is a better way? We need to set the default headers for the client
31
31
  // every time, because the token might be expired
32
32
  // And we cannot set the global dispatcher because it's shared with the runtime main thread.
33
- setDefaultHeaders(await app.getAuthorizationHeader())
33
+ setDefaultHeaders(await app.getAuthorizationHeaders())
34
34
  const compliancyMetadata = await getCompliancyMetadata({
35
35
  projectDir: appDir,
36
36
  runtime
@@ -281,7 +281,7 @@ async function flamegraphs (app, _opts) {
281
281
  query.alertId = alertId
282
282
  }
283
283
 
284
- const authHeaders = await app.getAuthorizationHeader()
284
+ const authHeaders = await app.getAuthorizationHeaders()
285
285
  const { statusCode, body } = await request(url, {
286
286
  method: 'POST',
287
287
  headers: {
@@ -350,7 +350,7 @@ async function flamegraphs (app, _opts) {
350
350
  const url = `${scalerUrl}/flamegraphs/${flamegraphId}/alerts`
351
351
  app.log.info({ flamegraphId, alerts: alertIds }, 'Attaching flamegraph to alerts')
352
352
 
353
- const authHeaders = await app.getAuthorizationHeader()
353
+ const authHeaders = await app.getAuthorizationHeaders()
354
354
  const { statusCode, body } = await request(url, {
355
355
  method: 'POST',
356
356
  headers: {
@@ -187,7 +187,7 @@ async function healthSignals (app, _opts) {
187
187
  const applicationId = app.instanceConfig?.applicationId
188
188
  const runtimeId = app.getRuntimeId()
189
189
  const timestamp = Date.now()
190
- const authHeaders = await app.getAuthorizationHeader()
190
+ const authHeaders = await app.getAuthorizationHeaders()
191
191
 
192
192
  const { statusCode, body } = await request(`${scalerUrl}/ready`, {
193
193
  method: 'POST',
@@ -207,7 +207,7 @@ async function healthSignals (app, _opts) {
207
207
  async function sendHealthSignals (rawSignals, batchStartedAt) {
208
208
  const scalerUrl = app.instanceConfig?.iccServices?.scaler?.url
209
209
  const applicationId = app.instanceConfig?.applicationId
210
- const authHeaders = await app.getAuthorizationHeader()
210
+ const authHeaders = await app.getAuthorizationHeaders()
211
211
 
212
212
  // Transform signals to the format expected by ICC LoadPredictor
213
213
  // Format: { serviceId: { elu: { options, workers: { workerId: { values: [[ts, val], ...] } } } } }
package/plugins/init.js CHANGED
@@ -8,7 +8,7 @@ async function initPlugin (app) {
8
8
  // There is a better way? We need to set the default headers for the client
9
9
  // every time, because the token might be expired
10
10
  // And we cannot set the global dispatcher because it's shared with the runtime main thread.
11
- setDefaultHeaders(await app.getAuthorizationHeader())
11
+ setDefaultHeaders(await app.getAuthorizationHeaders())
12
12
  const request = {
13
13
  podId,
14
14
  apiVersion: 'v3'
@@ -80,7 +80,7 @@ async function initPlugin (app) {
80
80
  app.watt = watt
81
81
  app.initApplication = initApplication
82
82
 
83
- const headers = await app.getAuthorizationHeader()
83
+ const headers = await app.getAuthorizationHeaders()
84
84
  await app.watt.updateSharedContext({ iccAuthHeaders: headers })
85
85
  }
86
86
 
@@ -60,13 +60,13 @@ async function metadata (app, _opts) {
60
60
  // There is a better way? We need to set the default headers for the client
61
61
  // every time, because the token might be expired
62
62
  // And we cannot set the global dispatcher because it's shared with the runtime main thread.
63
- setDefaultHeaders(await app.getAuthorizationHeader())
63
+ setDefaultHeaders(await app.getAuthorizationHeaders())
64
64
  await controlPlaneClient.saveApplicationInstanceState({
65
65
  id: app.instanceId,
66
66
  services,
67
67
  metadata: runtimeMetadata
68
68
  }, {
69
- headers: await app.getAuthorizationHeader()
69
+ headers: await app.getAuthorizationHeaders()
70
70
  })
71
71
  } catch (error) {
72
72
  app.log.error('Failed to save application state to Control Plane', error)
@@ -18,7 +18,7 @@ async function scheduler (app, _opts) {
18
18
  return
19
19
  }
20
20
  const cronClient = build(cronUrl)
21
- setDefaultHeaders(await app.getAuthorizationHeader())
21
+ setDefaultHeaders(await app.getAuthorizationHeaders())
22
22
 
23
23
  const jobs = config.scheduler || []
24
24
 
package/plugins/update.js CHANGED
@@ -72,7 +72,7 @@ async function updatePlugin (app) {
72
72
  app.log.info({ runtimeId }, `Connecting to updates websocket at ${wsUrl}`)
73
73
 
74
74
  try {
75
- const headers = await app.getAuthorizationHeader()
75
+ const headers = await app.getAuthorizationHeaders()
76
76
 
77
77
  socket = new WebSocket(wsUrl, { headers })
78
78
  await once(socket, 'open')
@@ -52,7 +52,7 @@ test('should send alert when service becomes unhealthy', async (t) => {
52
52
  let alertReceived = null
53
53
  let flamegraphReceived = null
54
54
 
55
- const getAuthorizationHeader = async (headers) => {
55
+ const getAuthorizationHeaders = async (headers) => {
56
56
  return { ...headers, authorization: 'Bearer test-token' }
57
57
  }
58
58
 
@@ -83,7 +83,7 @@ test('should send alert when service becomes unhealthy', async (t) => {
83
83
  })
84
84
 
85
85
  const app = await start()
86
- app.getAuthorizationHeader = getAuthorizationHeader
86
+ app.getAuthorizationHeaders = getAuthorizationHeaders
87
87
 
88
88
  t.after(async () => {
89
89
  await app.close()
@@ -146,7 +146,7 @@ test('should not send alert when application is healthy', async (t) => {
146
146
 
147
147
  let alertReceived = null
148
148
 
149
- const getAuthorizationHeader = async (headers) => {
149
+ const getAuthorizationHeaders = async (headers) => {
150
150
  return { ...headers, authorization: 'Bearer test-token' }
151
151
  }
152
152
 
@@ -168,7 +168,7 @@ test('should not send alert when application is healthy', async (t) => {
168
168
  })
169
169
 
170
170
  const app = await start()
171
- app.getAuthorizationHeader = getAuthorizationHeader
171
+ app.getAuthorizationHeaders = getAuthorizationHeaders
172
172
 
173
173
  t.after(async () => {
174
174
  await app.close()
@@ -211,7 +211,7 @@ test('should cache health data and include it in alerts', async (t) => {
211
211
 
212
212
  let alertReceived = null
213
213
 
214
- const getAuthorizationHeader = async (headers) => {
214
+ const getAuthorizationHeaders = async (headers) => {
215
215
  return { ...headers, authorization: 'Bearer test-token' }
216
216
  }
217
217
 
@@ -234,7 +234,7 @@ test('should cache health data and include it in alerts', async (t) => {
234
234
  })
235
235
 
236
236
  const app = await start()
237
- app.getAuthorizationHeader = getAuthorizationHeader
237
+ app.getAuthorizationHeaders = getAuthorizationHeaders
238
238
 
239
239
  t.after(async () => {
240
240
  await app.close()
@@ -328,7 +328,7 @@ test('should not fail when health info is missing', async (t) => {
328
328
 
329
329
  let alertReceived = null
330
330
 
331
- const getAuthorizationHeader = async (headers) => {
331
+ const getAuthorizationHeaders = async (headers) => {
332
332
  return { ...headers, authorization: 'Bearer test-token' }
333
333
  }
334
334
 
@@ -350,7 +350,7 @@ test('should not fail when health info is missing', async (t) => {
350
350
  })
351
351
 
352
352
  const app = await start()
353
- app.getAuthorizationHeader = getAuthorizationHeader
353
+ app.getAuthorizationHeaders = getAuthorizationHeaders
354
354
 
355
355
  t.after(async () => {
356
356
  await app.close()
@@ -371,7 +371,7 @@ test('should respect alert retention window', async (t) => {
371
371
 
372
372
  const alertsReceived = []
373
373
 
374
- const getAuthorizationHeader = async (headers) => {
374
+ const getAuthorizationHeaders = async (headers) => {
375
375
  return { ...headers, authorization: 'Bearer test-token' }
376
376
  }
377
377
 
@@ -397,7 +397,7 @@ test('should respect alert retention window', async (t) => {
397
397
 
398
398
  const app = await start()
399
399
 
400
- app.getAuthorizationHeader = getAuthorizationHeader
400
+ app.getAuthorizationHeaders = getAuthorizationHeaders
401
401
 
402
402
  t.after(async () => {
403
403
  await app.close()
@@ -495,7 +495,7 @@ test('should send alert when flamegraphs are disabled', async (t) => {
495
495
 
496
496
  let alertReceived = null
497
497
 
498
- const getAuthorizationHeader = async (headers) => {
498
+ const getAuthorizationHeaders = async (headers) => {
499
499
  return { ...headers, authorization: 'Bearer test-token' }
500
500
  }
501
501
 
@@ -519,7 +519,7 @@ test('should send alert when flamegraphs are disabled', async (t) => {
519
519
  })
520
520
 
521
521
  const app = await start()
522
- app.getAuthorizationHeader = getAuthorizationHeader
522
+ app.getAuthorizationHeaders = getAuthorizationHeaders
523
523
 
524
524
  t.after(async () => {
525
525
  await app.close()
@@ -577,7 +577,7 @@ test('should send alert when failed to send a flamegraph', async (t) => {
577
577
 
578
578
  let alertReceived = null
579
579
 
580
- const getAuthorizationHeader = async (headers) => {
580
+ const getAuthorizationHeaders = async (headers) => {
581
581
  return { ...headers, authorization: 'Bearer test-token' }
582
582
  }
583
583
 
@@ -604,7 +604,7 @@ test('should send alert when failed to send a flamegraph', async (t) => {
604
604
  })
605
605
 
606
606
  const app = await start()
607
- app.getAuthorizationHeader = getAuthorizationHeader
607
+ app.getAuthorizationHeaders = getAuthorizationHeaders
608
608
 
609
609
  t.after(async () => {
610
610
  await app.close()
@@ -662,7 +662,7 @@ test('should handle old runtime (< 3.18.0) health events', async (t) => {
662
662
 
663
663
  let alertReceived = null
664
664
 
665
- const getAuthorizationHeader = async (headers) => {
665
+ const getAuthorizationHeaders = async (headers) => {
666
666
  return { ...headers, authorization: 'Bearer test-token' }
667
667
  }
668
668
 
@@ -684,7 +684,7 @@ test('should handle old runtime (< 3.18.0) health events', async (t) => {
684
684
  })
685
685
 
686
686
  const app = await start()
687
- app.getAuthorizationHeader = getAuthorizationHeader
687
+ app.getAuthorizationHeaders = getAuthorizationHeaders
688
688
 
689
689
  // Mock the runtime version check to simulate old runtime
690
690
  const originalFn = app.watt.runtimeSupportsNewHealthMetrics
@@ -748,7 +748,7 @@ test('should attach one flamegraph to multiple alerts', async (t) => {
748
748
  const receivedFlamegraphs = []
749
749
  const receivedAttachedFlamegraphs = []
750
750
 
751
- const getAuthorizationHeader = async (headers) => {
751
+ const getAuthorizationHeaders = async (headers) => {
752
752
  return { ...headers, authorization: 'Bearer test-token' }
753
753
  }
754
754
 
@@ -792,7 +792,7 @@ test('should attach one flamegraph to multiple alerts', async (t) => {
792
792
  })
793
793
 
794
794
  const app = await start()
795
- app.getAuthorizationHeader = getAuthorizationHeader
795
+ app.getAuthorizationHeaders = getAuthorizationHeaders
796
796
 
797
797
  t.after(async () => {
798
798
  await app.close()
@@ -855,7 +855,7 @@ test('should send flamegraphs if attaching fails', async (t) => {
855
855
  const receivedAlerts = []
856
856
  const receivedFlamegraphs = []
857
857
 
858
- const getAuthorizationHeader = async (headers) => {
858
+ const getAuthorizationHeaders = async (headers) => {
859
859
  return { ...headers, authorization: 'Bearer test-token' }
860
860
  }
861
861
 
@@ -895,7 +895,7 @@ test('should send flamegraphs if attaching fails', async (t) => {
895
895
  })
896
896
 
897
897
  const app = await start()
898
- app.getAuthorizationHeader = getAuthorizationHeader
898
+ app.getAuthorizationHeaders = getAuthorizationHeaders
899
899
 
900
900
  t.after(async () => {
901
901
  await app.close()
@@ -956,7 +956,7 @@ test('should skip alerts during grace period but still cache health data', async
956
956
 
957
957
  let alertReceived = null
958
958
 
959
- const getAuthorizationHeader = async (headers) => {
959
+ const getAuthorizationHeaders = async (headers) => {
960
960
  return { ...headers, authorization: 'Bearer test-token' }
961
961
  }
962
962
 
@@ -979,7 +979,7 @@ test('should skip alerts during grace period but still cache health data', async
979
979
  })
980
980
 
981
981
  const app = await start()
982
- app.getAuthorizationHeader = getAuthorizationHeader
982
+ app.getAuthorizationHeaders = getAuthorizationHeaders
983
983
 
984
984
  t.after(async () => {
985
985
  await app.close()
@@ -1033,7 +1033,7 @@ test('should reset grace period when worker restarts', async (t) => {
1033
1033
 
1034
1034
  const alertsReceived = []
1035
1035
 
1036
- const getAuthorizationHeader = async (headers) => {
1036
+ const getAuthorizationHeaders = async (headers) => {
1037
1037
  return { ...headers, authorization: 'Bearer test-token' }
1038
1038
  }
1039
1039
 
@@ -1059,7 +1059,7 @@ test('should reset grace period when worker restarts', async (t) => {
1059
1059
  })
1060
1060
 
1061
1061
  const app = await start()
1062
- app.getAuthorizationHeader = getAuthorizationHeader
1062
+ app.getAuthorizationHeaders = getAuthorizationHeaders
1063
1063
 
1064
1064
  t.after(async () => {
1065
1065
  await app.close()
package/test/auth.test.js CHANGED
@@ -170,3 +170,48 @@ test('auth plugin does not reload when token is undefined', async (t) => {
170
170
  const reloadLogMessage = logMessages.find(msg => msg === 'JWT token expired, reloading')
171
171
  equal(reloadLogMessage, undefined, 'Should not attempt to reload undefined token')
172
172
  })
173
+
174
+ test('auth plugin sends ECS identity headers when running on ECS', async (t) => {
175
+ const originalEnv = { ...process.env }
176
+
177
+ t.after(() => {
178
+ process.env = originalEnv
179
+ })
180
+
181
+ // Mock ECS task metadata endpoint.
182
+ const metadata = fastify()
183
+ metadata.get('/task', async () => ({
184
+ TaskARN: 'arn:aws:ecs:us-east-1:123456789012:task/my-cluster/abcdef0123',
185
+ Cluster: 'my-cluster'
186
+ }))
187
+ await metadata.listen({ port: 0 })
188
+ const metadataUrl = `http://localhost:${metadata.server.address().port}`
189
+ t.after(() => metadata.close())
190
+
191
+ // Make detectProvider() pick ECS.
192
+ delete process.env.KUBERNETES_SERVICE_HOST
193
+ process.env.ECS_CONTAINER_METADATA_URI_V4 = metadataUrl
194
+
195
+ const server = fastify()
196
+ server.get('/', async (request) => {
197
+ return { headers: request.headers }
198
+ })
199
+ await server.listen({ port: 0 })
200
+ const url = `http://localhost:${server.server.address().port}`
201
+ t.after(() => server.close())
202
+
203
+ const app = createMockApp()
204
+ await authPlugin(app)
205
+
206
+ equal(app.machineIdentity?.id, 'abcdef0123', 'Task id should be the TaskARN suffix')
207
+ equal(app.machineIdentity?.namespace, 'my-cluster', 'Namespace should be the ECS cluster')
208
+ equal(app.token, undefined, 'No K8s JWT should be loaded on ECS')
209
+
210
+ const response = await request(url, { dispatcher: app.dispatcher })
211
+ const responseBody = await response.body.json()
212
+
213
+ equal(response.statusCode, 200)
214
+ equal(responseBody.headers['x-ecs-task-id'], 'abcdef0123')
215
+ equal(responseBody.headers['x-ecs-cluster'], 'my-cluster')
216
+ equal(responseBody.headers.authorization, undefined, 'No Authorization header on ECS')
217
+ })
@@ -21,7 +21,7 @@ test('should send health signals when service becomes unhealthy', async (t) => {
21
21
  const receivedFlamegraphReqs = []
22
22
  let receivedReadyReq = null
23
23
 
24
- const getAuthorizationHeader = async (headers) => {
24
+ const getAuthorizationHeaders = async (headers) => {
25
25
  return { ...headers, authorization: 'Bearer test-token' }
26
26
  }
27
27
 
@@ -61,7 +61,7 @@ test('should send health signals when service becomes unhealthy', async (t) => {
61
61
  })
62
62
 
63
63
  const app = await start()
64
- app.getAuthorizationHeader = getAuthorizationHeader
64
+ app.getAuthorizationHeaders = getAuthorizationHeaders
65
65
 
66
66
  t.after(async () => {
67
67
  await app.close()
package/test/init.test.js CHANGED
@@ -20,7 +20,7 @@ const createMockApp = (env = {}) => {
20
20
  warn: () => {},
21
21
  error: () => {}
22
22
  },
23
- getAuthorizationHeader: async () => 'Bearer test-token',
23
+ getAuthorizationHeaders: async () => 'Bearer test-token',
24
24
  logMessages
25
25
  }
26
26
  }
@@ -91,7 +91,7 @@ function createMockApp (port, includeScalerUrl = true, env = {}) {
91
91
  applicationId: 'test-application-id'
92
92
  },
93
93
  instanceId: 'test-pod-123',
94
- getAuthorizationHeader: async () => {
94
+ getAuthorizationHeaders: async () => {
95
95
  return { Authorization: 'Bearer test-token' }
96
96
  },
97
97
  getRuntimeId: () => {
@@ -18,7 +18,7 @@ function createMockApp (port, options = {}) {
18
18
  instanceConfig: {
19
19
  applicationId: 'test-application-id',
20
20
  },
21
- getAuthorizationHeader: async () => {
21
+ getAuthorizationHeaders: async () => {
22
22
  return { Authorization: 'Bearer test-token' }
23
23
  },
24
24
  getRuntimeId: () => {
@@ -1,7 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(node -e \"const schema = require\\(''''@platformatic/runtime/lib/schema.js''''\\); console.log\\(JSON.stringify\\(schema.default?.properties?.services?.items?.properties || schema.properties?.services?.items?.properties, null, 2\\)\\)\")"
5
- ]
6
- }
7
- }