settings.defaults.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. const Path = require('node:path')
  2. const os = require('node:os')
  3. const stream = require('node:stream')
  4. // TODO(24011): remove this after node 22 update
  5. stream.setDefaultHighWaterMark(false, 64 * 1024)
  6. const isPreEmptible = process.env.PREEMPTIBLE === 'TRUE'
  7. const CLSI_SERVER_ID = os.hostname().replace('-ctr', '')
  8. module.exports = {
  9. compileSizeLimit: process.env.COMPILE_SIZE_LIMIT || '7mb',
  10. processLifespanLimitMs:
  11. parseInt(process.env.PROCESS_LIFE_SPAN_LIMIT_MS) || 60 * 60 * 24 * 1000 * 2,
  12. catchErrors: process.env.CATCH_ERRORS === 'true',
  13. path: {
  14. compilesDir:
  15. process.env.CLSI_COMPILES_PATH || Path.resolve(__dirname, '../compiles'),
  16. outputDir:
  17. process.env.CLSI_OUTPUT_PATH || Path.resolve(__dirname, '../output'),
  18. clsiCacheDir:
  19. process.env.CLSI_CACHE_PATH || Path.resolve(__dirname, '../cache'),
  20. synctexBaseDir(projectId) {
  21. return Path.join(this.compilesDir, projectId)
  22. },
  23. },
  24. internal: {
  25. clsi: {
  26. port: 3013,
  27. host: process.env.LISTEN_ADDRESS || '127.0.0.1',
  28. },
  29. load_balancer_agent: {
  30. report_load: process.env.LOAD_BALANCER_AGENT_REPORT_LOAD !== 'false',
  31. load_port: 3048,
  32. local_port: 3049,
  33. allow_maintenance:
  34. (
  35. process.env.LOAD_BALANCER_AGENT_ALLOW_MAINTENANCE ?? ''
  36. ).toLowerCase() !== 'false',
  37. },
  38. },
  39. apis: {
  40. clsi: {
  41. // Internal requests (used by tests only at the time of writing).
  42. url: `http://${process.env.CLSI_HOST || '127.0.0.1'}:3013`,
  43. // External url prefix for output files, e.g. for requests via load-balancers.
  44. outputUrlPrefix: `${process.env.ZONE ? `/zone/${process.env.ZONE}` : ''}`,
  45. clsiServerId: process.env.CLSI_SERVER_ID || CLSI_SERVER_ID,
  46. downloadHost: process.env.DOWNLOAD_HOST || 'http://localhost:3013',
  47. },
  48. clsiPerf: {
  49. host: `${process.env.CLSI_PERF_HOST || '127.0.0.1'}:${
  50. process.env.CLSI_PERF_PORT || '3043'
  51. }`,
  52. },
  53. clsiCache: {
  54. enabled: !!(process.env.CLSI_CACHE_SHARDS || process.env.CLSI_CACHE_HOST),
  55. url: `http://${process.env.CLSI_CACHE_HOST}:3044`,
  56. shards: process.env.CLSI_CACHE_SHARDS
  57. ? JSON.parse(process.env.CLSI_CACHE_SHARDS)
  58. : [
  59. {
  60. url: `http://${process.env.CLSI_CACHE_HOST}:3044`,
  61. shard: 'cache',
  62. },
  63. ],
  64. },
  65. },
  66. smokeTest: process.env.SMOKE_TEST || false,
  67. project_cache_length_ms: 1000 * 60 * 60 * 24,
  68. parallelFileDownloads: process.env.FILESTORE_PARALLEL_FILE_DOWNLOADS || 1,
  69. filestoreDomainOveride: process.env.FILESTORE_DOMAIN_OVERRIDE,
  70. texliveImageNameOveride: process.env.TEX_LIVE_IMAGE_NAME_OVERRIDE,
  71. texliveOpenoutAny: process.env.TEXLIVE_OPENOUT_ANY,
  72. texliveMaxPrintLine: process.env.TEXLIVE_MAX_PRINT_LINE,
  73. enablePdfCaching: process.env.ENABLE_PDF_CACHING === 'true',
  74. enablePdfCachingDark: process.env.ENABLE_PDF_CACHING_DARK === 'true',
  75. pdfCachingMinChunkSize:
  76. parseInt(process.env.PDF_CACHING_MIN_CHUNK_SIZE, 10) || 1024,
  77. pdfCachingMaxProcessingTime:
  78. parseInt(process.env.PDF_CACHING_MAX_PROCESSING_TIME, 10) || 10 * 1000,
  79. pdfCachingEnableWorkerPool:
  80. process.env.PDF_CACHING_ENABLE_WORKER_POOL === 'true',
  81. pdfCachingWorkerPoolSize:
  82. parseInt(process.env.PDF_CACHING_WORKER_POOL_SIZE, 10) || 4,
  83. pdfCachingWorkerPoolBackLogLimit:
  84. parseInt(process.env.PDF_CACHING_WORKER_POOL_BACK_LOG_LIMIT, 10) || 40,
  85. compileConcurrencyLimit: isPreEmptible ? 32 : 64,
  86. }
  87. if (process.env.ALLOWED_COMPILE_GROUPS) {
  88. try {
  89. module.exports.allowedCompileGroups =
  90. process.env.ALLOWED_COMPILE_GROUPS.split(' ')
  91. } catch (error) {
  92. console.error(error, 'could not apply allowed compile group setting')
  93. process.exit(1)
  94. }
  95. }
  96. if ((process.env.DOCKER_RUNNER || process.env.SANDBOXED_COMPILES) === 'true') {
  97. module.exports.clsi = {
  98. dockerRunner: true,
  99. docker: {
  100. runtime: process.env.DOCKER_RUNTIME,
  101. image:
  102. process.env.TEXLIVE_IMAGE ||
  103. process.env.TEX_LIVE_DOCKER_IMAGE ||
  104. 'quay.io/sharelatex/texlive-full:2017.1',
  105. env: {
  106. HOME: '/tmp',
  107. CLSI: 1,
  108. },
  109. socketPath: '/var/run/docker.sock',
  110. user: process.env.TEXLIVE_IMAGE_USER || 'tex',
  111. },
  112. optimiseInDocker: true,
  113. expireProjectAfterIdleMs: 24 * 60 * 60 * 1000,
  114. checkProjectsIntervalMs: 10 * 60 * 1000,
  115. }
  116. try {
  117. // Override individual docker settings using path-based keys, e.g.:
  118. // compileGroupDockerConfigs = {
  119. // priority: { 'HostConfig.CpuShares': 100 }
  120. // beta: { 'dotted.path.here', 'value'}
  121. // }
  122. const compileGroupConfig = JSON.parse(
  123. process.env.COMPILE_GROUP_DOCKER_CONFIGS || '{}'
  124. )
  125. // Automatically clean up wordcount and synctex containers
  126. const defaultCompileGroupConfig = {
  127. wordcount: { 'HostConfig.AutoRemove': true },
  128. synctex: { 'HostConfig.AutoRemove': true },
  129. 'synctex-output': { 'HostConfig.AutoRemove': true },
  130. }
  131. module.exports.clsi.docker.compileGroupConfig = Object.assign(
  132. defaultCompileGroupConfig,
  133. compileGroupConfig
  134. )
  135. } catch (error) {
  136. console.error(error, 'could not apply compile group docker configs')
  137. process.exit(1)
  138. }
  139. let seccompProfilePath
  140. try {
  141. seccompProfilePath = Path.resolve(__dirname, '../seccomp/clsi-profile.json')
  142. module.exports.clsi.docker.seccomp_profile = JSON.stringify(
  143. JSON.parse(require('node:fs').readFileSync(seccompProfilePath))
  144. )
  145. } catch (error) {
  146. console.error(
  147. error,
  148. `could not load seccomp profile from ${seccompProfilePath}`
  149. )
  150. process.exit(1)
  151. }
  152. if (process.env.APPARMOR_PROFILE) {
  153. try {
  154. module.exports.clsi.docker.apparmor_profile = process.env.APPARMOR_PROFILE
  155. } catch (error) {
  156. console.error(error, 'could not apply apparmor profile setting')
  157. process.exit(1)
  158. }
  159. }
  160. if (process.env.ALLOWED_IMAGES) {
  161. try {
  162. module.exports.clsi.docker.allowedImages =
  163. process.env.ALLOWED_IMAGES.split(' ')
  164. } catch (error) {
  165. console.error(error, 'could not apply allowed images setting')
  166. process.exit(1)
  167. }
  168. }
  169. module.exports.path.synctexBaseDir = () => '/compile'
  170. module.exports.path.sandboxedCompilesHostDirCompiles =
  171. process.env.SANDBOXED_COMPILES_HOST_DIR_COMPILES ||
  172. process.env.SANDBOXED_COMPILES_HOST_DIR ||
  173. process.env.COMPILES_HOST_DIR
  174. if (!module.exports.path.sandboxedCompilesHostDirCompiles) {
  175. throw new Error(
  176. 'SANDBOXED_COMPILES enabled, but SANDBOXED_COMPILES_HOST_DIR_COMPILES not set'
  177. )
  178. }
  179. module.exports.path.sandboxedCompilesHostDirOutput =
  180. process.env.SANDBOXED_COMPILES_HOST_DIR_OUTPUT ||
  181. process.env.OUTPUT_HOST_DIR
  182. if (!module.exports.path.sandboxedCompilesHostDirOutput) {
  183. // TODO(das7pad): Enforce in a future major version of Server Pro.
  184. // throw new Error(
  185. // 'SANDBOXED_COMPILES enabled, but SANDBOXED_COMPILES_HOST_DIR_OUTPUT not set'
  186. // )
  187. }
  188. }