settings.defaults.js 6.4 KB

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