mongocryptd_manager.js 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.MongocryptdManager = void 0;
  4. const error_1 = require("../error");
  5. /**
  6. * @internal
  7. * An internal class that handles spawning a mongocryptd.
  8. */
  9. class MongocryptdManager {
  10. constructor(extraOptions = {}) {
  11. this.spawnPath = '';
  12. this.spawnArgs = [];
  13. this.uri =
  14. typeof extraOptions.mongocryptdURI === 'string' && extraOptions.mongocryptdURI.length > 0
  15. ? extraOptions.mongocryptdURI
  16. : MongocryptdManager.DEFAULT_MONGOCRYPTD_URI;
  17. this.bypassSpawn = !!extraOptions.mongocryptdBypassSpawn;
  18. if (Object.hasOwn(extraOptions, 'mongocryptdSpawnPath') && extraOptions.mongocryptdSpawnPath) {
  19. this.spawnPath = extraOptions.mongocryptdSpawnPath;
  20. }
  21. if (Object.hasOwn(extraOptions, 'mongocryptdSpawnArgs') &&
  22. Array.isArray(extraOptions.mongocryptdSpawnArgs)) {
  23. this.spawnArgs = this.spawnArgs.concat(extraOptions.mongocryptdSpawnArgs);
  24. }
  25. if (this.spawnArgs
  26. .filter(arg => typeof arg === 'string')
  27. .every(arg => arg.indexOf('--idleShutdownTimeoutSecs') < 0)) {
  28. this.spawnArgs.push('--idleShutdownTimeoutSecs', '60');
  29. }
  30. }
  31. /**
  32. * Will check to see if a mongocryptd is up. If it is not up, it will attempt
  33. * to spawn a mongocryptd in a detached process, and then wait for it to be up.
  34. */
  35. async spawn() {
  36. const cmdName = this.spawnPath || 'mongocryptd';
  37. // eslint-disable-next-line @typescript-eslint/no-require-imports
  38. const { spawn } = require('child_process');
  39. // Spawned with stdio: ignore and detached: true
  40. // to ensure child can outlive parent.
  41. this._child = spawn(cmdName, this.spawnArgs, {
  42. stdio: 'ignore',
  43. detached: true
  44. });
  45. this._child.on('error', () => {
  46. // From the FLE spec:
  47. // "The stdout and stderr of the spawned process MUST not be exposed in the driver
  48. // (e.g. redirect to /dev/null). Users can pass the argument --logpath to
  49. // extraOptions.mongocryptdSpawnArgs if they need to inspect mongocryptd logs.
  50. // If spawning is necessary, the driver MUST spawn mongocryptd whenever server
  51. // selection on the MongoClient to mongocryptd fails. If the MongoClient fails to
  52. // connect after spawning, the server selection error is propagated to the user."
  53. // The AutoEncrypter and MongoCryptdManager should work together to spawn
  54. // mongocryptd whenever necessary. Additionally, the `mongocryptd` intentionally
  55. // shuts down after 60s and gets respawned when necessary. We rely on server
  56. // selection timeouts when connecting to the `mongocryptd` to inform users that something
  57. // has been configured incorrectly. For those reasons, we suppress stderr from
  58. // the `mongocryptd` process and immediately unref the process.
  59. });
  60. // unref child to remove handle from event loop
  61. this._child.unref();
  62. }
  63. /**
  64. * @returns the result of `fn` or rejects with an error.
  65. */
  66. async withRespawn(fn) {
  67. try {
  68. const result = await fn();
  69. return result;
  70. }
  71. catch (err) {
  72. // If we are not bypassing spawning, then we should retry once on a MongoTimeoutError (server selection error)
  73. const shouldSpawn = err instanceof error_1.MongoNetworkTimeoutError && !this.bypassSpawn;
  74. if (!shouldSpawn) {
  75. throw err;
  76. }
  77. }
  78. await this.spawn();
  79. const result = await fn();
  80. return result;
  81. }
  82. }
  83. exports.MongocryptdManager = MongocryptdManager;
  84. MongocryptdManager.DEFAULT_MONGOCRYPTD_URI = 'mongodb://localhost:27020';
  85. //# sourceMappingURL=mongocryptd_manager.js.map