@shipfox/api-runners 5.0.0 → 7.0.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.
Files changed (400) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +80 -0
  3. package/dist/config.d.ts +5 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +54 -7
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/demand.d.ts +6 -6
  8. package/dist/core/demand.d.ts.map +1 -1
  9. package/dist/core/demand.js +10 -10
  10. package/dist/core/demand.js.map +1 -1
  11. package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
  12. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
  13. package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
  14. package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
  15. package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
  16. package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
  17. package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
  18. package/dist/core/entities/provisioner-token.d.ts +10 -2
  19. package/dist/core/entities/provisioner-token.d.ts.map +1 -1
  20. package/dist/core/entities/provisioner-token.js.map +1 -1
  21. package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +7 -6
  22. package/dist/core/entities/runner-instance.d.ts.map +1 -0
  23. package/dist/core/entities/runner-instance.js +3 -0
  24. package/dist/core/entities/runner-instance.js.map +1 -0
  25. package/dist/core/entities/runner-session.d.ts +4 -2
  26. package/dist/core/entities/runner-session.d.ts.map +1 -1
  27. package/dist/core/entities/runner-session.js.map +1 -1
  28. package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
  29. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
  30. package/dist/core/ephemeral-registration-tokens.js +12 -12
  31. package/dist/core/ephemeral-registration-tokens.js.map +1 -1
  32. package/dist/core/errors.d.ts +16 -4
  33. package/dist/core/errors.d.ts.map +1 -1
  34. package/dist/core/errors.js +22 -4
  35. package/dist/core/errors.js.map +1 -1
  36. package/dist/core/index.d.ts +9 -5
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/core/index.js +6 -3
  39. package/dist/core/index.js.map +1 -1
  40. package/dist/core/job-executions.d.ts +2 -0
  41. package/dist/core/job-executions.d.ts.map +1 -1
  42. package/dist/core/job-executions.js +1 -2
  43. package/dist/core/job-executions.js.map +1 -1
  44. package/dist/core/maintenance.d.ts +1 -1
  45. package/dist/core/maintenance.d.ts.map +1 -1
  46. package/dist/core/maintenance.js +5 -5
  47. package/dist/core/maintenance.js.map +1 -1
  48. package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
  49. package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
  50. package/dist/core/provisioner-capability-snapshots.js +10 -0
  51. package/dist/core/provisioner-capability-snapshots.js.map +1 -0
  52. package/dist/core/provisioner-tokens.d.ts +10 -0
  53. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  54. package/dist/core/provisioner-tokens.js +23 -1
  55. package/dist/core/provisioner-tokens.js.map +1 -1
  56. package/dist/core/rate-limit.d.ts.map +1 -1
  57. package/dist/core/rate-limit.js +2 -7
  58. package/dist/core/rate-limit.js.map +1 -1
  59. package/dist/core/runner-activation.d.ts +13 -0
  60. package/dist/core/runner-activation.d.ts.map +1 -0
  61. package/dist/core/runner-activation.js +36 -0
  62. package/dist/core/runner-activation.js.map +1 -0
  63. package/dist/core/runner-assignments.d.ts +6 -0
  64. package/dist/core/runner-assignments.d.ts.map +1 -0
  65. package/dist/core/runner-assignments.js +6 -0
  66. package/dist/core/runner-assignments.js.map +1 -0
  67. package/dist/core/runner-control-sessions.d.ts +46 -0
  68. package/dist/core/runner-control-sessions.d.ts.map +1 -0
  69. package/dist/core/runner-control-sessions.js +121 -0
  70. package/dist/core/runner-control-sessions.js.map +1 -0
  71. package/dist/core/runner-instances.d.ts +72 -0
  72. package/dist/core/runner-instances.d.ts.map +1 -0
  73. package/dist/core/runner-instances.js +165 -0
  74. package/dist/core/runner-instances.js.map +1 -0
  75. package/dist/core/runner-sessions.d.ts +8 -2
  76. package/dist/core/runner-sessions.d.ts.map +1 -1
  77. package/dist/core/runner-sessions.js +8 -5
  78. package/dist/core/runner-sessions.js.map +1 -1
  79. package/dist/db/db.d.ts +2698 -1054
  80. package/dist/db/db.d.ts.map +1 -1
  81. package/dist/db/db.js +9 -2
  82. package/dist/db/db.js.map +1 -1
  83. package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
  84. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  85. package/dist/db/ephemeral-registration-tokens.js +21 -21
  86. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  87. package/dist/db/index.d.ts +7 -4
  88. package/dist/db/index.d.ts.map +1 -1
  89. package/dist/db/index.js +5 -2
  90. package/dist/db/index.js.map +1 -1
  91. package/dist/db/job-executions.d.ts +6 -6
  92. package/dist/db/job-executions.d.ts.map +1 -1
  93. package/dist/db/job-executions.js +20 -19
  94. package/dist/db/job-executions.js.map +1 -1
  95. package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
  96. package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
  97. package/dist/db/provisioner-capability-snapshots.js +57 -0
  98. package/dist/db/provisioner-capability-snapshots.js.map +1 -0
  99. package/dist/db/provisioner-tokens.d.ts +7 -2
  100. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  101. package/dist/db/provisioner-tokens.js +59 -10
  102. package/dist/db/provisioner-tokens.js.map +1 -1
  103. package/dist/db/reservations.d.ts +18 -0
  104. package/dist/db/reservations.d.ts.map +1 -1
  105. package/dist/db/reservations.js +88 -1
  106. package/dist/db/reservations.js.map +1 -1
  107. package/dist/db/runner-assignments.d.ts +7 -0
  108. package/dist/db/runner-assignments.d.ts.map +1 -0
  109. package/dist/db/runner-assignments.js +55 -0
  110. package/dist/db/runner-assignments.js.map +1 -0
  111. package/dist/db/runner-instances.d.ts +84 -0
  112. package/dist/db/runner-instances.d.ts.map +1 -0
  113. package/dist/db/runner-instances.js +497 -0
  114. package/dist/db/runner-instances.js.map +1 -0
  115. package/dist/db/runner-sessions.d.ts +5 -0
  116. package/dist/db/runner-sessions.d.ts.map +1 -1
  117. package/dist/db/runner-sessions.js +48 -2
  118. package/dist/db/runner-sessions.js.map +1 -1
  119. package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
  120. package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
  121. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
  122. package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
  123. package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
  124. package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
  125. package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
  126. package/dist/db/schema/provisioner-tokens.d.ts +19 -1
  127. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
  128. package/dist/db/schema/provisioner-tokens.js +29 -3
  129. package/dist/db/schema/provisioner-tokens.js.map +1 -1
  130. package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
  131. package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
  132. package/dist/db/schema/runner-activation-tokens.js +27 -0
  133. package/dist/db/schema/runner-activation-tokens.js.map +1 -0
  134. package/dist/db/schema/runner-control-sessions.d.ts +338 -0
  135. package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
  136. package/dist/db/schema/runner-control-sessions.js +52 -0
  137. package/dist/db/schema/runner-control-sessions.js.map +1 -0
  138. package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +105 -34
  139. package/dist/db/schema/runner-instances.d.ts.map +1 -0
  140. package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +23 -14
  141. package/dist/db/schema/runner-instances.js.map +1 -0
  142. package/dist/db/schema/runner-sessions.d.ts +39 -5
  143. package/dist/db/schema/runner-sessions.d.ts.map +1 -1
  144. package/dist/db/schema/runner-sessions.js +13 -6
  145. package/dist/db/schema/runner-sessions.js.map +1 -1
  146. package/dist/db/schema/running-job-executions.d.ts +2 -2
  147. package/dist/db/schema/running-job-executions.js +4 -4
  148. package/dist/db/schema/running-job-executions.js.map +1 -1
  149. package/dist/index.d.ts +6 -2
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +45 -39
  152. package/dist/index.js.map +1 -1
  153. package/dist/installation-provisioning.d.ts +14 -0
  154. package/dist/installation-provisioning.d.ts.map +1 -0
  155. package/dist/installation-provisioning.js +7 -0
  156. package/dist/installation-provisioning.js.map +1 -0
  157. package/dist/metrics/index.d.ts +1 -1
  158. package/dist/metrics/index.d.ts.map +1 -1
  159. package/dist/metrics/index.js +1 -1
  160. package/dist/metrics/index.js.map +1 -1
  161. package/dist/metrics/instance.d.ts +11 -7
  162. package/dist/metrics/instance.d.ts.map +1 -1
  163. package/dist/metrics/instance.js +13 -7
  164. package/dist/metrics/instance.js.map +1 -1
  165. package/dist/presentation/auth/index.d.ts +1 -0
  166. package/dist/presentation/auth/index.d.ts.map +1 -1
  167. package/dist/presentation/auth/index.js +1 -0
  168. package/dist/presentation/auth/index.js.map +1 -1
  169. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
  170. package/dist/presentation/auth/provisioner-token-auth.js +5 -1
  171. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
  172. package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
  173. package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
  174. package/dist/presentation/auth/runner-control-session-auth.js +29 -0
  175. package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
  176. package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
  177. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
  178. package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
  179. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
  180. package/dist/presentation/dto/index.d.ts +1 -2
  181. package/dist/presentation/dto/index.d.ts.map +1 -1
  182. package/dist/presentation/dto/index.js +1 -2
  183. package/dist/presentation/dto/index.js.map +1 -1
  184. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  185. package/dist/presentation/dto/poll-demand.js +7 -1
  186. package/dist/presentation/dto/poll-demand.js.map +1 -1
  187. package/dist/presentation/dto/provisioner-token.d.ts +1 -0
  188. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
  189. package/dist/presentation/dto/provisioner-token.js +5 -1
  190. package/dist/presentation/dto/provisioner-token.js.map +1 -1
  191. package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
  192. package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
  193. package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
  194. package/dist/presentation/dto/runner-instances.js.map +1 -0
  195. package/dist/presentation/index.d.ts +2 -2
  196. package/dist/presentation/index.d.ts.map +1 -1
  197. package/dist/presentation/index.js +2 -2
  198. package/dist/presentation/index.js.map +1 -1
  199. package/dist/presentation/inter-module.d.ts +5 -0
  200. package/dist/presentation/inter-module.d.ts.map +1 -0
  201. package/dist/presentation/inter-module.js +43 -0
  202. package/dist/presentation/inter-module.js.map +1 -0
  203. package/dist/presentation/routes/assign-runner-instances.d.ts +2 -0
  204. package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -0
  205. package/dist/presentation/routes/assign-runner-instances.js +40 -0
  206. package/dist/presentation/routes/assign-runner-instances.js.map +1 -0
  207. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
  208. package/dist/presentation/routes/get-provisioner-me.js +5 -1
  209. package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
  210. package/dist/presentation/routes/heartbeat.d.ts +2 -1
  211. package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
  212. package/dist/presentation/routes/heartbeat.js +60 -50
  213. package/dist/presentation/routes/heartbeat.js.map +1 -1
  214. package/dist/presentation/routes/index.d.ts +3 -1
  215. package/dist/presentation/routes/index.d.ts.map +1 -1
  216. package/dist/presentation/routes/index.js +80 -51
  217. package/dist/presentation/routes/index.js.map +1 -1
  218. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  219. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  220. package/dist/presentation/routes/poll-demand.js +86 -45
  221. package/dist/presentation/routes/poll-demand.js.map +1 -1
  222. package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
  223. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
  224. package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
  225. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
  226. package/dist/presentation/routes/register.d.ts +2 -1
  227. package/dist/presentation/routes/register.d.ts.map +1 -1
  228. package/dist/presentation/routes/register.js +55 -52
  229. package/dist/presentation/routes/register.js.map +1 -1
  230. package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
  231. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
  232. package/dist/presentation/routes/report-runner-instances.js +37 -0
  233. package/dist/presentation/routes/report-runner-instances.js.map +1 -0
  234. package/dist/presentation/routes/request-job.d.ts +2 -1
  235. package/dist/presentation/routes/request-job.d.ts.map +1 -1
  236. package/dist/presentation/routes/request-job.js +39 -36
  237. package/dist/presentation/routes/request-job.js.map +1 -1
  238. package/dist/presentation/routes/runner-enrollment.d.ts +8 -0
  239. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
  240. package/dist/presentation/routes/runner-enrollment.js +212 -0
  241. package/dist/presentation/routes/runner-enrollment.js.map +1 -0
  242. package/dist/temporal/activities/index.d.ts +2 -2
  243. package/dist/temporal/activities/index.d.ts.map +1 -1
  244. package/dist/temporal/activities/index.js +2 -2
  245. package/dist/temporal/activities/index.js.map +1 -1
  246. package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
  247. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
  248. package/dist/temporal/activities/maintenance-activities.js +3 -3
  249. package/dist/temporal/activities/maintenance-activities.js.map +1 -1
  250. package/dist/temporal/workflows/index.bundle.js +3 -3
  251. package/dist/temporal/workflows/stuck-job-detector.js +2 -2
  252. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
  253. package/dist/tsconfig.test.tsbuildinfo +1 -1
  254. package/drizzle/0000_initial.sql +91 -24
  255. package/drizzle/meta/0000_snapshot.json +392 -63
  256. package/package.json +21 -16
  257. package/src/config.test.ts +0 -26
  258. package/src/config.ts +48 -7
  259. package/src/core/demand.test.ts +29 -29
  260. package/src/core/demand.ts +22 -22
  261. package/src/core/entities/ephemeral-registration-token.ts +1 -1
  262. package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
  263. package/src/core/entities/provisioner-token.ts +8 -2
  264. package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +6 -5
  265. package/src/core/entities/runner-session.ts +4 -2
  266. package/src/core/ephemeral-registration-tokens.test.ts +11 -11
  267. package/src/core/ephemeral-registration-tokens.ts +18 -20
  268. package/src/core/errors.ts +23 -4
  269. package/src/core/index.ts +37 -22
  270. package/src/core/job-executions.ts +3 -2
  271. package/src/core/maintenance.test.ts +13 -17
  272. package/src/core/maintenance.ts +5 -5
  273. package/src/core/provisioner-capability-snapshots.test.ts +35 -0
  274. package/src/core/provisioner-capability-snapshots.ts +12 -0
  275. package/src/core/provisioner-tokens.test.ts +20 -0
  276. package/src/core/provisioner-tokens.ts +33 -0
  277. package/src/core/rate-limit.test.ts +11 -27
  278. package/src/core/rate-limit.ts +3 -11
  279. package/src/core/runner-activation.ts +72 -0
  280. package/src/core/runner-assignments.ts +9 -0
  281. package/src/core/runner-control-sessions.ts +190 -0
  282. package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
  283. package/src/core/runner-instances.ts +301 -0
  284. package/src/core/runner-sessions.test.ts +112 -22
  285. package/src/core/runner-sessions.ts +25 -14
  286. package/src/db/db.ts +9 -2
  287. package/src/db/ephemeral-registration-tokens.test.ts +9 -9
  288. package/src/db/ephemeral-registration-tokens.ts +26 -26
  289. package/src/db/index.ts +30 -21
  290. package/src/db/job-executions.test.ts +13 -11
  291. package/src/db/job-executions.ts +33 -27
  292. package/src/db/provisioner-capability-snapshots.test.ts +175 -0
  293. package/src/db/provisioner-capability-snapshots.ts +130 -0
  294. package/src/db/provisioner-tokens.test.ts +139 -2
  295. package/src/db/provisioner-tokens.ts +123 -15
  296. package/src/db/reservations.test.ts +49 -0
  297. package/src/db/reservations.ts +146 -1
  298. package/src/db/runner-assignments.test.ts +163 -0
  299. package/src/db/runner-assignments.ts +115 -0
  300. package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +442 -353
  301. package/src/db/runner-instances.ts +922 -0
  302. package/src/db/runner-sessions.test.ts +19 -6
  303. package/src/db/runner-sessions.ts +78 -2
  304. package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
  305. package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
  306. package/src/db/schema/provisioner-tokens.ts +33 -2
  307. package/src/db/schema/runner-activation-tokens.ts +22 -0
  308. package/src/db/schema/runner-control-sessions.ts +46 -0
  309. package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +28 -27
  310. package/src/db/schema/runner-sessions.ts +11 -7
  311. package/src/db/schema/running-job-executions.ts +5 -5
  312. package/src/index.test.ts +26 -0
  313. package/src/index.ts +38 -27
  314. package/src/installation-provisioning.ts +14 -0
  315. package/src/metrics/index.ts +5 -5
  316. package/src/metrics/instance.ts +25 -14
  317. package/src/presentation/auth/index.ts +4 -0
  318. package/src/presentation/auth/provisioner-token-auth.ts +8 -4
  319. package/src/presentation/auth/runner-control-session-auth.ts +31 -0
  320. package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
  321. package/src/presentation/dto/index.ts +4 -8
  322. package/src/presentation/dto/poll-demand.ts +3 -1
  323. package/src/presentation/dto/provisioner-token.ts +5 -0
  324. package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
  325. package/src/presentation/index.ts +2 -1
  326. package/src/presentation/inter-module.test.ts +15 -0
  327. package/src/presentation/inter-module.ts +53 -0
  328. package/src/presentation/routes/assign-runner-instances.ts +49 -0
  329. package/src/presentation/routes/get-provisioner-me.ts +3 -4
  330. package/src/presentation/routes/heartbeat.test.ts +4 -3
  331. package/src/presentation/routes/heartbeat.ts +67 -64
  332. package/src/presentation/routes/index.ts +91 -44
  333. package/src/presentation/routes/list-active-runners.test.ts +23 -23
  334. package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
  335. package/src/presentation/routes/poll-demand-release.test.ts +4 -4
  336. package/src/presentation/routes/poll-demand.test.ts +161 -22
  337. package/src/presentation/routes/poll-demand.ts +105 -45
  338. package/src/presentation/routes/provisioner-me.test.ts +37 -3
  339. package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
  340. package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
  341. package/src/presentation/routes/register.test.ts +9 -5
  342. package/src/presentation/routes/register.ts +61 -57
  343. package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
  344. package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
  345. package/src/presentation/routes/request-job.test.ts +5 -3
  346. package/src/presentation/routes/request-job.ts +41 -37
  347. package/src/presentation/routes/runner-enrollment.test.ts +319 -0
  348. package/src/presentation/routes/runner-enrollment.ts +196 -0
  349. package/src/temporal/activities/index.ts +2 -2
  350. package/src/temporal/activities/maintenance-activities.test.ts +7 -7
  351. package/src/temporal/activities/maintenance-activities.ts +3 -3
  352. package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
  353. package/src/temporal/workflows/stuck-job-detector.ts +2 -2
  354. package/test/env.ts +1 -3
  355. package/test/factories/ephemeral-registration-token.ts +2 -2
  356. package/test/factories/provisioner-token.ts +3 -1
  357. package/test/factories/runner-instance.ts +57 -0
  358. package/test/factories/runner-session.ts +3 -1
  359. package/test/fixtures/auth-inter-module.ts +30 -0
  360. package/test/globalSetup.ts +4 -1
  361. package/test/index.ts +2 -1
  362. package/tsconfig.build.tsbuildinfo +1 -1
  363. package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
  364. package/dist/core/entities/provisioned-runner.js +0 -3
  365. package/dist/core/entities/provisioned-runner.js.map +0 -1
  366. package/dist/core/provisioned-runners.d.ts +0 -67
  367. package/dist/core/provisioned-runners.d.ts.map +0 -1
  368. package/dist/core/provisioned-runners.js +0 -162
  369. package/dist/core/provisioned-runners.js.map +0 -1
  370. package/dist/db/provisioned-runners.d.ts +0 -79
  371. package/dist/db/provisioned-runners.d.ts.map +0 -1
  372. package/dist/db/provisioned-runners.js +0 -466
  373. package/dist/db/provisioned-runners.js.map +0 -1
  374. package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
  375. package/dist/db/schema/provisioned-runners.js.map +0 -1
  376. package/dist/presentation/dto/mint-registration-tokens.d.ts +0 -5
  377. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +0 -1
  378. package/dist/presentation/dto/mint-registration-tokens.js +0 -19
  379. package/dist/presentation/dto/mint-registration-tokens.js.map +0 -1
  380. package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
  381. package/dist/presentation/dto/provisioned-runners.js.map +0 -1
  382. package/dist/presentation/routes/mint-registration-tokens.d.ts +0 -2
  383. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +0 -1
  384. package/dist/presentation/routes/mint-registration-tokens.js +0 -74
  385. package/dist/presentation/routes/mint-registration-tokens.js.map +0 -1
  386. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
  387. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
  388. package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
  389. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
  390. package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
  391. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
  392. package/dist/presentation/routes/report-provisioned-runners.js +0 -37
  393. package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
  394. package/src/core/provisioned-runners.ts +0 -301
  395. package/src/db/provisioned-runners.ts +0 -886
  396. package/src/presentation/dto/mint-registration-tokens.ts +0 -35
  397. package/src/presentation/routes/mint-registration-tokens.test.ts +0 -453
  398. package/src/presentation/routes/mint-registration-tokens.ts +0 -82
  399. package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
  400. package/test/factories/provisioned-runner.ts +0 -78
@@ -1,886 +0,0 @@
1
- import {logger} from '@shipfox/node-opentelemetry';
2
- import {canonicalizeLabels} from '@shipfox/runner-labels';
3
- import {
4
- and,
5
- asc,
6
- desc,
7
- eq,
8
- exists,
9
- gt,
10
- inArray,
11
- isNotNull,
12
- isNull,
13
- lt,
14
- notExists,
15
- notInArray,
16
- or,
17
- type SQL,
18
- sql,
19
- } from 'drizzle-orm';
20
- import {alias} from 'drizzle-orm/pg-core';
21
- import type {ProvisionedRunner, ProvisionedRunnerState} from '#core/entities/provisioned-runner.js';
22
- import type {Tx} from './db.js';
23
- import {db} from './db.js';
24
- import {
25
- listRunningJobExecutionsByProvisionedRunnerTx,
26
- type ProvisionedRunnerBoundJobExecution,
27
- } from './job-executions.js';
28
- import {releaseReservationUnits} from './reservations.js';
29
- import {ephemeralRegistrationTokens} from './schema/ephemeral-registration-tokens.js';
30
- import {provisionedRunners, toProvisionedRunner} from './schema/provisioned-runners.js';
31
- import {provisionerTokens} from './schema/provisioner-tokens.js';
32
- import {runnerSessions} from './schema/runner-sessions.js';
33
- import {runningJobExecutions} from './schema/running-job-executions.js';
34
-
35
- export const terminalStates = [
36
- 'stopped',
37
- 'failed',
38
- 'terminated',
39
- ] as const satisfies readonly ProvisionedRunnerState[];
40
- export const activeStates = [
41
- 'starting',
42
- 'running',
43
- 'stopping',
44
- ] as const satisfies readonly ProvisionedRunnerState[];
45
- export const divergenceCountStates = ['starting', 'running'] as const satisfies readonly Extract<
46
- ProvisionedRunnerState,
47
- 'starting' | 'running'
48
- >[];
49
-
50
- export type ProvisionedRunnerTerminateIntentReason = 'job-cancelled';
51
-
52
- export interface ProvisionedRunnerTerminateIntent {
53
- provisionedRunnerId: string;
54
- reason: ProvisionedRunnerTerminateIntentReason;
55
- }
56
-
57
- export interface ActiveProvisionedRunnerTemplateCount {
58
- templateKey: string;
59
- state: (typeof divergenceCountStates)[number];
60
- count: number;
61
- }
62
-
63
- export interface ProvisionedRunnerReportEvent {
64
- provisionedRunnerId: string;
65
- reservationId: string | null;
66
- templateKey: string | null;
67
- labels: string[];
68
- state: ProvisionedRunnerState;
69
- reason: string | null;
70
- runnerSessionId: string | null;
71
- providerKind: string | null;
72
- reportedAt: Date;
73
- }
74
-
75
- export interface ReportProvisionedRunnersParams {
76
- workspaceId: string;
77
- provisionerId: string;
78
- events: ProvisionedRunnerReportEvent[];
79
- }
80
-
81
- export interface ReconcileProvisionedRunnersParams {
82
- workspaceId: string;
83
- provisionerId: string;
84
- observedProvisionedRunnerIds: string[];
85
- terminateGraceSeconds: number;
86
- }
87
-
88
- export interface ReconcileProvisionedRunnersDbResult {
89
- observedRows: ProvisionedRunner[];
90
- boundJobExecutionsByProvisionedRunnerId: Map<string, ProvisionedRunnerBoundJobExecution>;
91
- absentIds: string[];
92
- reservationsReleased: number;
93
- }
94
-
95
- export interface ReapStaleProvisionedRunnersResult {
96
- reaped: number;
97
- reservationsReleased: number;
98
- }
99
-
100
- interface ProvisionedRunnerReportRow extends ProvisionedRunnerReportEvent {
101
- startedAt: Date | null;
102
- stoppingAt: Date | null;
103
- stoppedAt: Date | null;
104
- failedAt: Date | null;
105
- terminatedAt: Date | null;
106
- }
107
-
108
- type ProvisionedRunnerMilestoneColumn =
109
- | typeof provisionedRunners.startedAt
110
- | typeof provisionedRunners.stoppingAt
111
- | typeof provisionedRunners.stoppedAt
112
- | typeof provisionedRunners.failedAt
113
- | typeof provisionedRunners.terminatedAt;
114
-
115
- export async function reportProvisionedRunners(params: ReportProvisionedRunnersParams): Promise<{
116
- accepted: number;
117
- reservationsReleased: number;
118
- terminateIntentsHonored: ProvisionedRunnerTerminateIntent[];
119
- }> {
120
- if (params.events.length === 0)
121
- return {accepted: 0, reservationsReleased: 0, terminateIntentsHonored: []};
122
-
123
- return await db().transaction(async (tx) => {
124
- const receivedAt = new Date();
125
- const aggregatedEvents = aggregateEvents(params.events, receivedAt);
126
- const hasTerminalEvent = aggregatedEvents.some((event) => isTerminalState(event.state));
127
-
128
- if (hasTerminalEvent) {
129
- await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);
130
- }
131
-
132
- const events = await hydrateRunnerSessionIdsFromConsumedTokens(tx, params, aggregatedEvents);
133
- const terminateIntentsHonored = await listTerminateIntentsHonoredByTerminatedReportsTx(
134
- tx,
135
- params,
136
- events,
137
- );
138
-
139
- const values = events.map((event) => ({
140
- workspaceId: params.workspaceId,
141
- provisionerId: params.provisionerId,
142
- provisionedRunnerId: event.provisionedRunnerId,
143
- reservationId: event.reservationId,
144
- templateKey: event.templateKey,
145
- labels: [...canonicalizeLabels(event.labels)],
146
- state: event.state,
147
- reason: event.reason,
148
- runnerSessionId: event.runnerSessionId,
149
- providerKind: event.providerKind,
150
- reportedAt: event.reportedAt > receivedAt ? receivedAt : event.reportedAt,
151
- startedAt: event.startedAt,
152
- stoppingAt: event.stoppingAt,
153
- stoppedAt: event.stoppedAt,
154
- failedAt: event.failedAt,
155
- terminatedAt: event.terminatedAt,
156
- }));
157
-
158
- await tx
159
- .insert(provisionedRunners)
160
- .values(values)
161
- .onConflictDoUpdate({
162
- target: [
163
- provisionedRunners.workspaceId,
164
- provisionedRunners.provisionerId,
165
- provisionedRunners.provisionedRunnerId,
166
- ],
167
- set: {
168
- reservationId: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN coalesce(excluded.reservation_id, ${provisionedRunners.reservationId}) ELSE ${provisionedRunners.reservationId} END`,
169
- templateKey: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN coalesce(excluded.template_key, ${provisionedRunners.templateKey}) ELSE ${provisionedRunners.templateKey} END`,
170
- labels: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN excluded.labels ELSE ${provisionedRunners.labels} END`,
171
- state: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN excluded.state ELSE ${provisionedRunners.state} END`,
172
- reason: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN excluded.reason ELSE ${provisionedRunners.reason} END`,
173
- runnerSessionId: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN coalesce(${provisionedRunners.runnerSessionId}, excluded.runner_session_id) ELSE ${provisionedRunners.runnerSessionId} END`,
174
- providerKind: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN coalesce(excluded.provider_kind, ${provisionedRunners.providerKind}) ELSE ${provisionedRunners.providerKind} END`,
175
- reportedAt: sql`CASE WHEN ${provisionedRunnerProjectionUpdateCondition()} THEN excluded.reported_at ELSE ${provisionedRunners.reportedAt} END`,
176
- startedAt: firstObservedAt(provisionedRunners.startedAt, sql`excluded.started_at`),
177
- stoppingAt: firstObservedAt(provisionedRunners.stoppingAt, sql`excluded.stopping_at`),
178
- stoppedAt: firstObservedAt(provisionedRunners.stoppedAt, sql`excluded.stopped_at`),
179
- failedAt: firstObservedAt(provisionedRunners.failedAt, sql`excluded.failed_at`),
180
- terminatedAt: firstObservedAt(
181
- provisionedRunners.terminatedAt,
182
- sql`excluded.terminated_at`,
183
- ),
184
- updatedAt: sql`now()`,
185
- },
186
- setWhere: sql`
187
- ${provisionedRunnerProjectionUpdateCondition()}
188
- OR ${provisionedRunnerMilestoneUpdateCondition()}
189
- `,
190
- });
191
-
192
- const reservationsReleased = hasTerminalEvent
193
- ? await releaseTerminalProvisionedRunnerReservations(tx, params, events)
194
- : 0;
195
-
196
- return {accepted: events.length, reservationsReleased, terminateIntentsHonored};
197
- });
198
- }
199
-
200
- export async function listActiveProvisionedRunnerCountsByTemplateTx(
201
- tx: Tx,
202
- params: {workspaceId: string; provisionerId: string},
203
- ): Promise<ActiveProvisionedRunnerTemplateCount[]> {
204
- const rows = await tx
205
- .select({
206
- templateKey: provisionedRunners.templateKey,
207
- state: provisionedRunners.state,
208
- count: sql<number>`count(*)::int`,
209
- })
210
- .from(provisionedRunners)
211
- .where(
212
- and(
213
- eq(provisionedRunners.workspaceId, params.workspaceId),
214
- eq(provisionedRunners.provisionerId, params.provisionerId),
215
- inArray(provisionedRunners.state, divergenceCountStates),
216
- isNotNull(provisionedRunners.templateKey),
217
- ),
218
- )
219
- .groupBy(provisionedRunners.templateKey, provisionedRunners.state);
220
-
221
- return rows.flatMap((row) =>
222
- row.templateKey && isDivergenceCountState(row.state)
223
- ? [{templateKey: row.templateKey, state: row.state, count: row.count}]
224
- : [],
225
- );
226
- }
227
-
228
- export async function listActiveProvisionedRunners(params: {
229
- workspaceId: string;
230
- windowSeconds: number;
231
- limit?: number;
232
- }): Promise<ProvisionedRunner[]> {
233
- const rows = await db()
234
- .select()
235
- .from(provisionedRunners)
236
- .where(
237
- and(
238
- eq(provisionedRunners.workspaceId, params.workspaceId),
239
- inArray(provisionedRunners.state, activeStates),
240
- sql`${provisionedRunners.updatedAt} > now() - (${params.windowSeconds} || ' seconds')::interval`,
241
- ),
242
- )
243
- .orderBy(desc(provisionedRunners.updatedAt), desc(provisionedRunners.id))
244
- .limit(params.limit ?? 1000);
245
-
246
- return rows.map(toProvisionedRunner);
247
- }
248
-
249
- export async function listProvisionerTerminateIntents(params: {
250
- workspaceId: string;
251
- provisionerId: string;
252
- limit: number;
253
- }): Promise<string[]> {
254
- return await db().transaction(async (tx) => {
255
- const rows = await listProvisionerTerminateIntentRowsTx(tx, params);
256
- return rows.map((row) => row.provisionedRunnerId);
257
- });
258
- }
259
-
260
- export async function listProvisionerTerminateIntentRowsTx(
261
- tx: Tx,
262
- params: {
263
- workspaceId: string;
264
- provisionerId: string;
265
- limit: number;
266
- },
267
- ): Promise<ProvisionedRunnerTerminateIntent[]> {
268
- const rows = await provisionerTerminateIntentsQuery(tx, params)
269
- .orderBy(asc(provisionedRunners.provisionedRunnerId))
270
- .limit(params.limit + 1);
271
-
272
- const truncated = rows.length > params.limit;
273
- const returnedRows = truncated ? rows.slice(0, params.limit) : rows;
274
- if (truncated) {
275
- logger().warn(
276
- {
277
- workspaceId: params.workspaceId,
278
- provisionerId: params.provisionerId,
279
- limit: params.limit,
280
- returnedCount: returnedRows.length,
281
- },
282
- 'provisioner terminate intents truncated by poll-demand limit',
283
- );
284
- }
285
-
286
- return returnedRows;
287
- }
288
-
289
- function provisionerTerminateIntentsQuery(
290
- tx: Tx,
291
- params: {workspaceId: string; provisionerId: string; provisionedRunnerIds?: string[]},
292
- ) {
293
- const newerRunningJobExecutions = alias(runningJobExecutions, 'newer_running_jobs');
294
-
295
- return tx
296
- .select({
297
- provisionedRunnerId: provisionedRunners.provisionedRunnerId,
298
- reason: sql<ProvisionedRunnerTerminateIntentReason>`'job-cancelled'`,
299
- })
300
- .from(runningJobExecutions)
301
- .innerJoin(
302
- provisionedRunners,
303
- and(
304
- eq(provisionedRunners.workspaceId, runningJobExecutions.workspaceId),
305
- eq(provisionedRunners.provisionerId, runningJobExecutions.provisionerId),
306
- eq(provisionedRunners.provisionedRunnerId, runningJobExecutions.provisionedRunnerId),
307
- ),
308
- )
309
- .where(
310
- and(
311
- eq(runningJobExecutions.workspaceId, params.workspaceId),
312
- eq(runningJobExecutions.provisionerId, params.provisionerId),
313
- isNotNull(runningJobExecutions.cancellationRequestedAt),
314
- inArray(provisionedRunners.state, activeStates),
315
- params.provisionedRunnerIds && params.provisionedRunnerIds.length > 0
316
- ? inArray(provisionedRunners.provisionedRunnerId, params.provisionedRunnerIds)
317
- : undefined,
318
- notExists(
319
- tx
320
- .select({id: newerRunningJobExecutions.id})
321
- .from(newerRunningJobExecutions)
322
- .where(
323
- and(
324
- eq(newerRunningJobExecutions.workspaceId, runningJobExecutions.workspaceId),
325
- eq(newerRunningJobExecutions.provisionerId, runningJobExecutions.provisionerId),
326
- eq(
327
- newerRunningJobExecutions.provisionedRunnerId,
328
- runningJobExecutions.provisionedRunnerId,
329
- ),
330
- or(
331
- gt(newerRunningJobExecutions.startedAt, runningJobExecutions.startedAt),
332
- and(
333
- eq(newerRunningJobExecutions.startedAt, runningJobExecutions.startedAt),
334
- gt(
335
- newerRunningJobExecutions.jobExecutionId,
336
- runningJobExecutions.jobExecutionId,
337
- ),
338
- ),
339
- ),
340
- ),
341
- ),
342
- ),
343
- ),
344
- )
345
- .groupBy(provisionedRunners.provisionedRunnerId);
346
- }
347
-
348
- async function listTerminateIntentsHonoredByTerminatedReportsTx(
349
- tx: Tx,
350
- params: ReportProvisionedRunnersParams,
351
- events: ProvisionedRunnerReportEvent[],
352
- ): Promise<ProvisionedRunnerTerminateIntent[]> {
353
- const terminatedProvisionedRunnerIds = [
354
- ...new Set(
355
- events
356
- .filter((event) => event.state === 'terminated')
357
- .map((event) => event.provisionedRunnerId),
358
- ),
359
- ];
360
- if (terminatedProvisionedRunnerIds.length === 0) return [];
361
-
362
- return await provisionerTerminateIntentsQuery(tx, {
363
- workspaceId: params.workspaceId,
364
- provisionerId: params.provisionerId,
365
- provisionedRunnerIds: terminatedProvisionedRunnerIds,
366
- }).orderBy(asc(provisionedRunners.provisionedRunnerId));
367
- }
368
-
369
- export async function reconcileProvisionedRunners(
370
- params: ReconcileProvisionedRunnersParams,
371
- ): Promise<ReconcileProvisionedRunnersDbResult> {
372
- const observedProvisionedRunnerIds = [...new Set(params.observedProvisionedRunnerIds)];
373
-
374
- return await db().transaction(async (tx) => {
375
- await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);
376
-
377
- let absentIds: string[] = [];
378
- let reservationsReleased = 0;
379
- if (observedProvisionedRunnerIds.length > 0) {
380
- const staleAbsentRows = await tx
381
- .select({
382
- id: provisionedRunners.id,
383
- provisionedRunnerId: provisionedRunners.provisionedRunnerId,
384
- })
385
- .from(provisionedRunners)
386
- .where(
387
- and(
388
- eq(provisionedRunners.workspaceId, params.workspaceId),
389
- eq(provisionedRunners.provisionerId, params.provisionerId),
390
- inArray(provisionedRunners.state, activeStates),
391
- lt(
392
- provisionedRunners.reportedAt,
393
- sql`now() - (${params.terminateGraceSeconds} || ' seconds')::interval`,
394
- ),
395
- notInArray(provisionedRunners.provisionedRunnerId, observedProvisionedRunnerIds),
396
- ),
397
- );
398
-
399
- if (staleAbsentRows.length > 0) {
400
- const updated = await tx
401
- .update(provisionedRunners)
402
- .set({
403
- state: 'terminated',
404
- terminatedAt: sql`coalesce(${provisionedRunners.terminatedAt}, now())`,
405
- updatedAt: sql`now()`,
406
- })
407
- .where(
408
- and(
409
- inArray(
410
- provisionedRunners.id,
411
- staleAbsentRows.map((row) => row.id),
412
- ),
413
- inArray(provisionedRunners.state, activeStates),
414
- lt(
415
- provisionedRunners.reportedAt,
416
- sql`now() - (${params.terminateGraceSeconds} || ' seconds')::interval`,
417
- ),
418
- ),
419
- )
420
- .returning({provisionedRunnerId: provisionedRunners.provisionedRunnerId});
421
-
422
- absentIds = updated.map((row) => row.provisionedRunnerId);
423
- reservationsReleased = await releaseTerminalProvisionedRunnerReservationsByIds(tx, {
424
- workspaceId: params.workspaceId,
425
- provisionerId: params.provisionerId,
426
- provisionedRunnerIds: absentIds,
427
- });
428
- }
429
- }
430
-
431
- const observedRows =
432
- observedProvisionedRunnerIds.length === 0
433
- ? []
434
- : (
435
- await tx
436
- .select()
437
- .from(provisionedRunners)
438
- .where(
439
- and(
440
- eq(provisionedRunners.workspaceId, params.workspaceId),
441
- eq(provisionedRunners.provisionerId, params.provisionerId),
442
- inArray(provisionedRunners.provisionedRunnerId, observedProvisionedRunnerIds),
443
- ),
444
- )
445
- ).map(toProvisionedRunner);
446
-
447
- const boundJobExecutions = await listRunningJobExecutionsByProvisionedRunnerTx(tx, {
448
- workspaceId: params.workspaceId,
449
- provisionerId: params.provisionerId,
450
- provisionedRunnerIds: observedProvisionedRunnerIds,
451
- });
452
-
453
- return {
454
- observedRows,
455
- boundJobExecutionsByProvisionedRunnerId: new Map(
456
- boundJobExecutions.map((jobExecution) => [jobExecution.provisionedRunnerId, jobExecution]),
457
- ),
458
- absentIds,
459
- reservationsReleased,
460
- };
461
- });
462
- }
463
-
464
- export async function reapStaleProvisionedRunners(params: {
465
- thresholdSeconds: number;
466
- limit: number;
467
- }): Promise<ReapStaleProvisionedRunnersResult> {
468
- const cutoff = staleProvisionedRunnerCutoff(params.thresholdSeconds);
469
-
470
- return await db().transaction(async (tx) => {
471
- const candidateRows = await tx
472
- .select({
473
- id: provisionedRunners.id,
474
- workspaceId: provisionedRunners.workspaceId,
475
- provisionerId: provisionedRunners.provisionerId,
476
- provisionedRunnerId: provisionedRunners.provisionedRunnerId,
477
- })
478
- .from(provisionedRunners)
479
- .where(staleProvisionedRunnerWhere(tx, cutoff))
480
- .orderBy(asc(provisionedRunners.updatedAt), asc(provisionedRunners.id))
481
- .limit(params.limit);
482
-
483
- if (candidateRows.length === 0) return {reaped: 0, reservationsReleased: 0};
484
-
485
- const workspaceIds = [...new Set(candidateRows.map((row) => row.workspaceId))].sort();
486
- for (const workspaceId of workspaceIds) {
487
- await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${workspaceId}))`);
488
- }
489
-
490
- const updatedRows = await tx
491
- .update(provisionedRunners)
492
- .set({
493
- state: 'failed',
494
- reason: 'stale-provisioner',
495
- failedAt: sql`coalesce(${provisionedRunners.failedAt}, now())`,
496
- updatedAt: sql`now()`,
497
- })
498
- .where(
499
- and(
500
- inArray(
501
- provisionedRunners.id,
502
- candidateRows.map((row) => row.id),
503
- ),
504
- staleProvisionedRunnerWhere(tx, cutoff),
505
- ),
506
- )
507
- .returning({
508
- workspaceId: provisionedRunners.workspaceId,
509
- provisionerId: provisionedRunners.provisionerId,
510
- provisionedRunnerId: provisionedRunners.provisionedRunnerId,
511
- });
512
-
513
- let reservationsReleased = 0;
514
- for (const group of groupProvisionedRunnerIds(updatedRows)) {
515
- reservationsReleased += await releaseTerminalProvisionedRunnerReservationsByIds(tx, {
516
- workspaceId: group.workspaceId,
517
- provisionerId: group.provisionerId,
518
- provisionedRunnerIds: group.provisionedRunnerIds,
519
- requireUnlinkedSession: false,
520
- });
521
- }
522
-
523
- return {reaped: updatedRows.length, reservationsReleased};
524
- });
525
- }
526
-
527
- async function releaseTerminalProvisionedRunnerReservations(
528
- tx: Tx,
529
- params: ReportProvisionedRunnersParams,
530
- events: ProvisionedRunnerReportEvent[],
531
- ): Promise<number> {
532
- const terminalEvents = events.filter((event) => isTerminalState(event.state));
533
- if (terminalEvents.length === 0) return 0;
534
-
535
- return await releaseTerminalProvisionedRunnerReservationsByIds(tx, {
536
- workspaceId: params.workspaceId,
537
- provisionerId: params.provisionerId,
538
- provisionedRunnerIds: terminalEvents.map((event) => event.provisionedRunnerId),
539
- });
540
- }
541
-
542
- async function releaseTerminalProvisionedRunnerReservationsByIds(
543
- tx: Tx,
544
- params: {
545
- workspaceId: string;
546
- provisionerId: string;
547
- provisionedRunnerIds: string[];
548
- requireUnlinkedSession?: boolean;
549
- },
550
- ): Promise<number> {
551
- if (params.provisionedRunnerIds.length === 0) return 0;
552
-
553
- const rows = await tx
554
- .select({
555
- id: provisionedRunners.id,
556
- reservationId: provisionedRunners.reservationId,
557
- })
558
- .from(provisionedRunners)
559
- .where(
560
- and(
561
- eq(provisionedRunners.workspaceId, params.workspaceId),
562
- eq(provisionedRunners.provisionerId, params.provisionerId),
563
- inArray(provisionedRunners.provisionedRunnerId, params.provisionedRunnerIds),
564
- inArray(provisionedRunners.state, terminalStates),
565
- isNotNull(provisionedRunners.reservationId),
566
- params.requireUnlinkedSession === false
567
- ? undefined
568
- : isNull(provisionedRunners.runnerSessionId),
569
- isNull(provisionedRunners.reservationReleasedAt),
570
- ),
571
- );
572
-
573
- if (rows.length === 0) return 0;
574
-
575
- const updated = await tx
576
- .update(provisionedRunners)
577
- .set({reservationReleasedAt: sql`now()`, updatedAt: sql`now()`})
578
- .where(
579
- and(
580
- inArray(
581
- provisionedRunners.id,
582
- rows.map((row) => row.id),
583
- ),
584
- params.requireUnlinkedSession === false
585
- ? undefined
586
- : isNull(provisionedRunners.runnerSessionId),
587
- isNull(provisionedRunners.reservationReleasedAt),
588
- ),
589
- )
590
- .returning({reservationId: provisionedRunners.reservationId});
591
-
592
- const releasesByReservationId = new Map<string, number>();
593
- for (const row of updated) {
594
- if (!row.reservationId) continue;
595
- releasesByReservationId.set(
596
- row.reservationId,
597
- (releasesByReservationId.get(row.reservationId) ?? 0) + 1,
598
- );
599
- }
600
-
601
- if (releasesByReservationId.size === 0) return 0;
602
-
603
- return await releaseReservationUnits(tx, {
604
- workspaceId: params.workspaceId,
605
- provisionerId: params.provisionerId,
606
- releases: [...releasesByReservationId].map(([reservationId, count]) => ({
607
- reservationId,
608
- count,
609
- })),
610
- });
611
- }
612
-
613
- function staleProvisionedRunnerCutoff(thresholdSeconds: number): SQL {
614
- return sql`now() - (${thresholdSeconds} || ' seconds')::interval`;
615
- }
616
-
617
- function staleProvisionedRunnerWhere(tx: Tx, cutoff: SQL): SQL<boolean> {
618
- return and(
619
- inArray(provisionedRunners.state, activeStates),
620
- lt(provisionedRunners.reportedAt, cutoff),
621
- lt(provisionedRunners.updatedAt, cutoff),
622
- exists(
623
- tx
624
- .select({id: provisionerTokens.id})
625
- .from(provisionerTokens)
626
- .where(
627
- and(
628
- eq(provisionerTokens.id, provisionedRunners.provisionerId),
629
- eq(provisionerTokens.workspaceId, provisionedRunners.workspaceId),
630
- or(isNull(provisionerTokens.lastSeenAt), lt(provisionerTokens.lastSeenAt, cutoff)),
631
- ),
632
- ),
633
- ),
634
- notExists(
635
- tx
636
- .select({id: runningJobExecutions.id})
637
- .from(runningJobExecutions)
638
- .where(
639
- and(
640
- eq(runningJobExecutions.workspaceId, provisionedRunners.workspaceId),
641
- eq(runningJobExecutions.provisionerId, provisionedRunners.provisionerId),
642
- eq(runningJobExecutions.provisionedRunnerId, provisionedRunners.provisionedRunnerId),
643
- ),
644
- ),
645
- ),
646
- notExists(
647
- tx
648
- .select({id: runnerSessions.id})
649
- .from(runnerSessions)
650
- .where(
651
- and(
652
- eq(runnerSessions.workspaceId, provisionedRunners.workspaceId),
653
- eq(runnerSessions.provisionerId, provisionedRunners.provisionerId),
654
- eq(runnerSessions.provisionedRunnerId, provisionedRunners.provisionedRunnerId),
655
- sql`${runnerSessions.updatedAt} >= ${cutoff}`,
656
- ),
657
- ),
658
- ),
659
- ) as SQL<boolean>;
660
- }
661
-
662
- function groupProvisionedRunnerIds(
663
- rows: Array<{workspaceId: string; provisionerId: string; provisionedRunnerId: string}>,
664
- ): Array<{workspaceId: string; provisionerId: string; provisionedRunnerIds: string[]}> {
665
- const groups = new Map<
666
- string,
667
- {workspaceId: string; provisionerId: string; provisionedRunnerIds: string[]}
668
- >();
669
- for (const row of rows) {
670
- const key = `${row.workspaceId}:${row.provisionerId}`;
671
- const group = groups.get(key) ?? {
672
- workspaceId: row.workspaceId,
673
- provisionerId: row.provisionerId,
674
- provisionedRunnerIds: [],
675
- };
676
- group.provisionedRunnerIds.push(row.provisionedRunnerId);
677
- groups.set(key, group);
678
- }
679
- return [...groups.values()];
680
- }
681
-
682
- async function hydrateRunnerSessionIdsFromConsumedTokens(
683
- tx: Tx,
684
- params: ReportProvisionedRunnersParams,
685
- events: ProvisionedRunnerReportRow[],
686
- ): Promise<ProvisionedRunnerReportRow[]> {
687
- const provisionedRunnerIds = [...new Set(events.map((event) => event.provisionedRunnerId))];
688
- if (provisionedRunnerIds.length === 0) return events;
689
-
690
- const tokenRows = await tx
691
- .select({
692
- provisionedRunnerId: ephemeralRegistrationTokens.provisionedRunnerId,
693
- consumedSessionId: ephemeralRegistrationTokens.consumedSessionId,
694
- })
695
- .from(ephemeralRegistrationTokens)
696
- .where(
697
- and(
698
- eq(ephemeralRegistrationTokens.workspaceId, params.workspaceId),
699
- eq(ephemeralRegistrationTokens.provisionerId, params.provisionerId),
700
- inArray(ephemeralRegistrationTokens.provisionedRunnerId, provisionedRunnerIds),
701
- isNotNull(ephemeralRegistrationTokens.consumedSessionId),
702
- ),
703
- )
704
- .orderBy(
705
- desc(ephemeralRegistrationTokens.consumedAt),
706
- desc(ephemeralRegistrationTokens.createdAt),
707
- );
708
-
709
- const consumedSessionIdsByProvisionedRunnerId = new Map<string, string>();
710
- for (const row of tokenRows) {
711
- if (!row.consumedSessionId) continue;
712
- if (consumedSessionIdsByProvisionedRunnerId.has(row.provisionedRunnerId)) continue;
713
- consumedSessionIdsByProvisionedRunnerId.set(row.provisionedRunnerId, row.consumedSessionId);
714
- }
715
-
716
- if (consumedSessionIdsByProvisionedRunnerId.size === 0) return events;
717
-
718
- return events.map((event) => {
719
- const consumedSessionId = consumedSessionIdsByProvisionedRunnerId.get(
720
- event.provisionedRunnerId,
721
- );
722
- if (!consumedSessionId || event.runnerSessionId === consumedSessionId) return event;
723
- return {...event, runnerSessionId: consumedSessionId};
724
- });
725
- }
726
-
727
- function aggregateEvents(
728
- events: ProvisionedRunnerReportEvent[],
729
- receivedAt: Date,
730
- ): ProvisionedRunnerReportRow[] {
731
- const byProvisionedRunnerId = new Map<string, ProvisionedRunnerReportRow>();
732
- for (const rawEvent of events) {
733
- const event = toProvisionedRunnerReportRow(rawEvent, receivedAt);
734
- const existing = byProvisionedRunnerId.get(event.provisionedRunnerId);
735
- if (existing) mergeMilestones(existing, event);
736
- if (!existing || compareProvisionedRunnerReportEvents(event, existing) > 0) {
737
- byProvisionedRunnerId.set(
738
- event.provisionedRunnerId,
739
- existing ? mergeProjectionMetadata(event, existing) : event,
740
- );
741
- }
742
- }
743
- return [...byProvisionedRunnerId.values()];
744
- }
745
-
746
- function toProvisionedRunnerReportRow(
747
- event: ProvisionedRunnerReportEvent,
748
- receivedAt: Date,
749
- ): ProvisionedRunnerReportRow {
750
- const reportedAt = event.reportedAt > receivedAt ? receivedAt : event.reportedAt;
751
- return {
752
- ...event,
753
- reportedAt,
754
- startedAt: event.state === 'running' ? reportedAt : null,
755
- stoppingAt: event.state === 'stopping' ? reportedAt : null,
756
- stoppedAt: event.state === 'stopped' ? reportedAt : null,
757
- failedAt: event.state === 'failed' ? reportedAt : null,
758
- terminatedAt: event.state === 'terminated' ? reportedAt : null,
759
- };
760
- }
761
-
762
- function mergeMilestones(target: ProvisionedRunnerReportRow, source: ProvisionedRunnerReportRow) {
763
- target.startedAt = earliestDate(target.startedAt, source.startedAt);
764
- target.stoppingAt = earliestDate(target.stoppingAt, source.stoppingAt);
765
- target.stoppedAt = earliestDate(target.stoppedAt, source.stoppedAt);
766
- target.failedAt = earliestDate(target.failedAt, source.failedAt);
767
- target.terminatedAt = earliestDate(target.terminatedAt, source.terminatedAt);
768
- }
769
-
770
- function mergeProjectionMetadata(
771
- event: ProvisionedRunnerReportRow,
772
- existing: ProvisionedRunnerReportRow,
773
- ): ProvisionedRunnerReportRow {
774
- return {
775
- ...event,
776
- reservationId: event.reservationId ?? existing.reservationId,
777
- templateKey: event.templateKey ?? existing.templateKey,
778
- runnerSessionId: event.runnerSessionId ?? existing.runnerSessionId,
779
- providerKind: event.providerKind ?? existing.providerKind,
780
- ...pickMilestones(existing),
781
- };
782
- }
783
-
784
- function pickMilestones(event: ProvisionedRunnerReportRow) {
785
- return {
786
- startedAt: event.startedAt,
787
- stoppingAt: event.stoppingAt,
788
- stoppedAt: event.stoppedAt,
789
- failedAt: event.failedAt,
790
- terminatedAt: event.terminatedAt,
791
- };
792
- }
793
-
794
- function earliestDate(a: Date | null, b: Date | null): Date | null {
795
- if (!a) return b;
796
- if (!b) return a;
797
- return a < b ? a : b;
798
- }
799
-
800
- function compareProvisionedRunnerReportEvents(
801
- a: ProvisionedRunnerReportEvent,
802
- b: ProvisionedRunnerReportEvent,
803
- ): number {
804
- const timeDelta = a.reportedAt.getTime() - b.reportedAt.getTime();
805
- const rankDelta = getProvisionedRunnerStateRank(a.state) - getProvisionedRunnerStateRank(b.state);
806
- if (rankDelta !== 0) return rankDelta;
807
- return timeDelta;
808
- }
809
-
810
- function getProvisionedRunnerStateRank(state: ProvisionedRunnerState): number {
811
- switch (state) {
812
- case 'starting':
813
- return 1;
814
- case 'running':
815
- return 2;
816
- case 'stopping':
817
- return 3;
818
- case 'stopped':
819
- return 4;
820
- case 'failed':
821
- return 5;
822
- case 'terminated':
823
- return 6;
824
- }
825
- }
826
-
827
- function provisionedRunnerStateRank(state: SQL | typeof provisionedRunners.state): SQL<number> {
828
- return sql<number>`
829
- CASE ${state}
830
- WHEN 'starting' THEN 1
831
- WHEN 'running' THEN 2
832
- WHEN 'stopping' THEN 3
833
- WHEN 'stopped' THEN 4
834
- WHEN 'failed' THEN 5
835
- WHEN 'terminated' THEN 6
836
- ELSE 0
837
- END
838
- `;
839
- }
840
-
841
- function provisionedRunnerProjectionUpdateCondition(): SQL<boolean> {
842
- return sql<boolean>`
843
- ${provisionedRunnerStateRank(sql`excluded.state`)} > ${provisionedRunnerStateRank(provisionedRunners.state)}
844
- OR (
845
- ${provisionedRunnerStateRank(sql`excluded.state`)} = ${provisionedRunnerStateRank(provisionedRunners.state)}
846
- AND excluded.reported_at >= ${provisionedRunners.reportedAt}
847
- )
848
- `;
849
- }
850
-
851
- function provisionedRunnerMilestoneUpdateCondition(): SQL<boolean> {
852
- return sql<boolean>`
853
- ${milestoneNeedsUpdate(provisionedRunners.startedAt, sql`excluded.started_at`)}
854
- OR ${milestoneNeedsUpdate(provisionedRunners.stoppingAt, sql`excluded.stopping_at`)}
855
- OR ${milestoneNeedsUpdate(provisionedRunners.stoppedAt, sql`excluded.stopped_at`)}
856
- OR ${milestoneNeedsUpdate(provisionedRunners.failedAt, sql`excluded.failed_at`)}
857
- OR ${milestoneNeedsUpdate(provisionedRunners.terminatedAt, sql`excluded.terminated_at`)}
858
- `;
859
- }
860
-
861
- function milestoneNeedsUpdate(
862
- current: SQL | ProvisionedRunnerMilestoneColumn,
863
- incoming: SQL,
864
- ): SQL<boolean> {
865
- return sql<boolean>`${incoming} IS NOT NULL AND (${current} IS NULL OR ${incoming} < ${current})`;
866
- }
867
-
868
- function firstObservedAt(current: SQL | ProvisionedRunnerMilestoneColumn, incoming: SQL) {
869
- return sql`
870
- CASE
871
- WHEN ${incoming} IS NULL THEN ${current}
872
- WHEN ${current} IS NULL THEN ${incoming}
873
- ELSE least(${current}, ${incoming})
874
- END
875
- `;
876
- }
877
-
878
- export function isTerminalState(state: ProvisionedRunnerState): boolean {
879
- return terminalStates.includes(state as (typeof terminalStates)[number]);
880
- }
881
-
882
- function isDivergenceCountState(
883
- state: ProvisionedRunnerState,
884
- ): state is (typeof divergenceCountStates)[number] {
885
- return divergenceCountStates.includes(state as (typeof divergenceCountStates)[number]);
886
- }