UNPKG

22.7 kBJavaScriptView Raw
1"use strict";
2
3var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4
5function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
7var co = require('co'),
8 f = require('util').format,
9 mkdirp = require('mkdirp'),
10 rimraf = require('rimraf'),
11 Logger = require('./logger'),
12 CoreServer = require('mongodb-core').Server,
13 spawn = require('child_process').spawn;
14
15var clone = function clone(o) {
16 var obj = {};for (var name in o) {
17 obj[name] = o[name];
18 }return obj;
19};
20
21var Server = (function () {
22 function Server(binary, options, clientOptions) {
23 _classCallCheck(this, Server);
24
25 options = options || {};
26 this.options = clone(options);
27
28 // Server state
29 this.state = 'stopped';
30
31 // Create logger instance
32 this.logger = Logger('Server', options);
33
34 // Unpack default runtime information
35 this.binary = binary || 'mongod';
36 // Current process
37 this.process = null;
38 // Current command
39 this.command = null;
40 // Credentials store
41 this.credentials = [];
42 // Additional internal client options
43 this.clientOptions = clientOptions || {};
44
45 // Default values for host and port if not set
46 this.options.bind_ip = typeof this.options.bind_ip == 'string' ? this.options.bind_ip : 'localhost';
47 this.options.port = typeof this.options.port == 'number' ? this.options.port : 27017;
48 }
49
50 _createClass(Server, [{
51 key: 'discover',
52 value: function discover() {
53 var self = this;
54
55 return new Promise(function (resolve, reject) {
56 co(regeneratorRuntime.mark(function _callee() {
57 var proc, stdout, stderr;
58 return regeneratorRuntime.wrap(function _callee$(_context) {
59 while (1) {
60 switch (_context.prev = _context.next) {
61 case 0:
62 proc = spawn(self.binary, ['--version']);
63
64 // Do we have a valid proc
65
66 if (!(!proc || !proc.stdout || !proc.stderr)) {
67 _context.next = 3;
68 break;
69 }
70
71 return _context.abrupt('return', reject(new Error(f('failed to start [%s --version]', self.binary))));
72
73 case 3:
74
75 // Variables receiving data
76 stdout = '';
77 stderr = '';
78 // Get the stdout
79
80 proc.stdout.on('data', function (data) {
81 stdout += data;
82 });
83 // Get the stderr
84 proc.stderr.on('data', function (data) {
85 stderr += data;
86 });
87 // Got an error
88 proc.on('error', function (err) {
89 reject(err);
90 });
91 // Process terminated
92 proc.on('close', function (code) {
93 // Perform version match
94 var versionMatch = stdout.match(/[0-9]+\.[0-9]+\.[0-9]+/);
95
96 // Check if we have ssl
97 var sslMatch = stdout.match(/ssl/i);
98
99 // Resolve the server version
100 resolve({
101 version: versionMatch.toString().split('.').map(function (x) {
102 return parseInt(x, 10);
103 }),
104 ssl: sslMatch != null
105 });
106 });
107
108 case 9:
109 case 'end':
110 return _context.stop();
111 }
112 }
113 }, _callee, this);
114 })).catch(reject);
115 });
116 }
117 }, {
118 key: 'instance',
119 value: function instance(credentials, options) {
120 var self = this;
121 options = options || {};
122 options = clone(options);
123
124 return new Promise(function (resolve, reject) {
125 co(regeneratorRuntime.mark(function _callee2() {
126 var opt, s;
127 return regeneratorRuntime.wrap(function _callee2$(_context2) {
128 while (1) {
129 switch (_context2.prev = _context2.next) {
130 case 0:
131 // Copy the basic options
132 opt = clone(self.clientOptions);
133
134 opt.host = self.options.bind_ip;
135 opt.port = self.options.port;
136 opt.connectionTimeout = 5000;
137 opt.socketTimeout = 5000;
138 opt.pool = 1;
139
140 // Ensure we only connect once and emit any error caught
141 opt.reconnect = false;
142 opt.emitError = true;
143
144 // Create an instance
145 s = new CoreServer(opt);
146
147 s.on('error', function (err) {
148 reject(err);
149 });
150
151 s.on('close', function (err) {
152 reject(err);
153 });
154
155 s.on('timeout', function (err) {
156 reject(err);
157 });
158
159 s.on('connect', function (_server) {
160 // Do we have credentials
161 var authenticate = function authenticate(_server, _credentials, _callback) {
162 if (!_credentials) return _callback();
163 // Perform authentication using provided
164 // credentials for this command
165 _server.auth(_credentials.provider, _credentials.db, _credentials.user, _credentials.password, _callback);
166 };
167
168 // Perform any necessary authentication
169 authenticate(_server, credentials, function (err) {
170 if (err) return reject(err);
171 resolve(_server);
172 });
173 });
174
175 // Connect
176 s.connect();
177
178 case 14:
179 case 'end':
180 return _context2.stop();
181 }
182 }
183 }, _callee2, this);
184 }));
185 });
186 }
187 }, {
188 key: 'executeCommand',
189 value: function executeCommand(ns, command, credentials, options) {
190 var self = this;
191 options = options || {};
192 options = clone(options);
193
194 return new Promise(function (resolve, reject) {
195 co(regeneratorRuntime.mark(function _callee3() {
196 var opt, executeCommand, s;
197 return regeneratorRuntime.wrap(function _callee3$(_context3) {
198 while (1) {
199 switch (_context3.prev = _context3.next) {
200 case 0:
201 // Copy the basic options
202 opt = clone(self.clientOptions);
203
204 opt.host = self.options.bind_ip;
205 opt.port = self.options.port;
206 opt.connectionTimeout = 5000;
207 opt.socketTimeout = 0;
208 opt.pool = 1;
209
210 // Ensure we only connect once and emit any error caught
211 opt.reconnect = false;
212 opt.emitError = true;
213
214 // Execute command
215
216 executeCommand = function executeCommand(_ns, _command, _credentials, _server) {
217 // Do we have credentials
218 var authenticate = function authenticate(_server, _credentials, _callback) {
219 if (!_credentials) return _callback();
220 // Perform authentication using provided
221 // credentials for this command
222 _server.auth(_credentials.provider, _credentials.db, _credentials.user, _credentials.password, _callback);
223 };
224
225 authenticate(_server, _credentials, function (err) {
226 if (err && options.ignoreError) return resolve({ ok: 1 });
227 // If we had an error return
228 if (err) {
229 _server.destroy();
230 return reject(err);
231 }
232
233 // Execute command
234 _server.command(_ns, _command, function (err, r) {
235 // Destroy the connection
236 _server.destroy();
237 // Return an error
238 if (err && options.ignoreError) return resolve({ ok: 1 });
239 if (err) return reject(err);
240 // Return the ismaster command
241 resolve(r.result);
242 });
243 });
244 };
245
246 // Create an instance
247
248 s = new CoreServer(opt);
249
250 s.on('error', function (err) {
251 if (options.ignoreError) return resolve({ ok: 1 });
252 if (options.reExecuteOnError) {
253 options.reExecuteOnError = false;
254 return executeCommand(ns, command, credentials, s);
255 }
256
257 reject(err);
258 });
259
260 s.on('close', function (err) {
261 if (options.ignoreError) return resolve({ ok: 1 });
262 if (options.reExecuteOnError) {
263 options.reExecuteOnError = false;
264 return executeCommand(ns, command, credentials, s);
265 }
266
267 reject(err);
268 });
269
270 s.on('timeout', function (err) {
271 if (options.ignoreError) return resolve({ ok: 1 });
272 reject(err);
273 });
274
275 s.on('connect', function (_server) {
276 executeCommand(ns, command, credentials, _server);
277 });
278
279 // Connect
280 s.connect();
281
282 case 15:
283 case 'end':
284 return _context3.stop();
285 }
286 }
287 }, _callee3, this);
288 })).catch(function (err) {
289 if (options.ignoreError) return resolve({ ok: 1 });
290 reject(err);
291 });
292 });
293 }
294 }, {
295 key: 'start',
296 value: function start() {
297 var self = this;
298
299 return new Promise(function (resolve, reject) {
300 co(regeneratorRuntime.mark(function _callee4() {
301 var result, version, errors, options, commandOptions, name, i, o, commandLine, stdout, stderr;
302 return regeneratorRuntime.wrap(function _callee4$(_context4) {
303 while (1) {
304 switch (_context4.prev = _context4.next) {
305 case 0:
306 _context4.next = 2;
307 return self.discover();
308
309 case 2:
310 result = _context4.sent;
311 version = result.version;
312
313 // All errors found during validation
314
315 errors = [];
316
317 // Ensure basic parameters
318
319 if (!self.options.dbpath) {
320 errors.push(new Error('dbpath is required'));
321 }
322
323 // Do we have any errors
324
325 if (!(errors.length > 0)) {
326 _context4.next = 8;
327 break;
328 }
329
330 return _context4.abrupt('return', reject(errors));
331
332 case 8:
333
334 // Figure out what special options we need to pass into the boot script
335 // Removing any non-compatible parameters etc.
336 if (version[0] == 3 && version[1] >= 0 && version[1] <= 2) {} else if (version[0] == 3 && version[1] >= 2) {} else if (version[0] == 2 && version[1] <= 6) {}
337
338 // Merge in all the options
339 options = clone(self.options);
340
341 // Build command options list
342
343 commandOptions = [];
344
345 // Do we have a 2.2 server, then we don't support setParameter
346
347 if (version[0] == 2 && version[1] == 2) {
348 delete options['setParameter'];
349 }
350
351 // Go over all the options
352 for (name in options) {
353 if (options[name] == null) {
354 commandOptions.push(f('--%s', name));
355 } else if (Array.isArray(options[name])) {
356 // We have an array of a specific option f.ex --setParameter
357 for (i = 0; i < options[name].length; i++) {
358 o = options[name][i];
359
360 if (o == null) {
361 commandOptions.push(f('--%s', name));
362 } else {
363 commandOptions.push(f('--%s=%s', name, options[name][i]));
364 }
365 }
366 } else {
367 commandOptions.push(f('--%s=%s', name, options[name]));
368 }
369 }
370
371 // Command line
372 commandLine = f('%s %s', self.binary, commandOptions.join(' '));
373 // console.log("----------------------------------------------------------------------------")
374 // console.log(commandLine)
375
376 if (self.logger.isInfo()) {
377 self.logger.info(f('started mongod with [%s]', commandLine));
378 }
379
380 // Spawn a mongod process
381 self.process = spawn(self.binary, commandOptions);
382
383 // Variables receiving data
384 stdout = '';
385 stderr = '';
386
387 // Get the stdout
388
389 self.process.stdout.on('data', function (data) {
390 stdout += data.toString();
391 // console.log(data.toString())
392
393 //
394 // Only emit event at start
395 if (self.state == 'stopped') {
396 if (stdout.indexOf('waiting for connections') != -1 || stdout.indexOf('connection accepted') != -1) {
397 // Mark state as running
398 self.state = 'running';
399 // Resolve
400 resolve();
401 }
402 }
403 });
404
405 // Get the stderr
406 self.process.stderr.on('data', function (data) {
407 stderr += data;
408 });
409
410 // Got an error
411 self.process.on('error', function (err) {
412 reject(new Error({ error: error, stdout: stdout, stderr: stderr }));
413 });
414
415 // Process terminated
416 self.process.on('close', function (code) {
417 if (self.state == 'stopped' && stdout == '' || code != 0) {
418 return reject(new Error(f('failed to start mongod with options %s', commandOptions)));
419 }
420
421 self.state = 'stopped';
422 });
423
424 case 22:
425 case 'end':
426 return _context4.stop();
427 }
428 }
429 }, _callee4, this);
430 })).catch(reject);
431 });
432 }
433
434 /*
435 * Retrieve the ismaster for this server
436 */
437
438 }, {
439 key: 'ismaster',
440 value: function ismaster() {
441 var self = this;
442
443 return new Promise(function (resolve, reject) {
444 co(regeneratorRuntime.mark(function _callee5() {
445 var opt, s;
446 return regeneratorRuntime.wrap(function _callee5$(_context5) {
447 while (1) {
448 switch (_context5.prev = _context5.next) {
449 case 0:
450 // Copy the basic options
451 opt = clone(self.clientOptions);
452
453 opt.host = self.options.bind_ip;
454 opt.port = self.options.port;
455 opt.connectionTimeout = 5000;
456 opt.socketTimeout = 5000;
457 opt.pool = 1;
458
459 // Ensure we only connect once and emit any error caught
460 opt.reconnect = false;
461 opt.emitError = true;
462
463 // Create an instance
464 s = new CoreServer(opt);
465 // Add listeners
466
467 s.on('error', function (err) {
468 reject(err);
469 });
470
471 s.on('close', function (err) {
472 reject(err);
473 });
474
475 s.on('timeout', function (err) {
476 reject(err);
477 });
478
479 s.on('connect', function (_server) {
480 _server.command('system.$cmd', {
481 ismaster: true
482 }, function (err, r) {
483 // Destroy the connection
484 _server.destroy();
485 // Return an error
486 if (err) return callback(err);
487 // Return the ismaster command
488 resolve(r.result);
489 });
490 });
491
492 // Connect
493 s.connect();
494
495 case 14:
496 case 'end':
497 return _context5.stop();
498 }
499 }
500 }, _callee5, this);
501 })).catch(reject);
502 });
503 }
504
505 /*
506 * Purge the db directory
507 */
508
509 }, {
510 key: 'purge',
511 value: function purge() {
512 var self = this;
513
514 return new Promise(function (resolve, reject) {
515 co(regeneratorRuntime.mark(function _callee6() {
516 return regeneratorRuntime.wrap(function _callee6$(_context6) {
517 while (1) {
518 switch (_context6.prev = _context6.next) {
519 case 0:
520 try {
521 // Delete the dbpath
522 rimraf.sync(self.options.dbpath);
523 } catch (err) {}
524
525 try {
526 // Re-Create the directory
527 mkdirp.sync(self.options.dbpath);
528 } catch (err) {}
529
530 // Return
531 resolve();
532
533 case 3:
534 case 'end':
535 return _context6.stop();
536 }
537 }
538 }, _callee6, this);
539 })).catch(reject);
540 });
541 }
542 }, {
543 key: 'stop',
544 value: function stop(signal) {
545 var self = this;
546 signal = typeof signal == 'number' ? signal : signals.SIGTERM;
547
548 return new Promise(function (resolve, reject) {
549 co(regeneratorRuntime.mark(function _callee7() {
550 return regeneratorRuntime.wrap(function _callee7$(_context7) {
551 while (1) {
552 switch (_context7.prev = _context7.next) {
553 case 0:
554 if (!(!self.process || self.state == 'stopped')) {
555 _context7.next = 3;
556 break;
557 }
558
559 // Set process to stopped
560 self.state = 'stopped';
561 // Return
562 return _context7.abrupt('return', resolve());
563
564 case 3:
565
566 // Wait for service to stop
567 self.process.on('close', function () {
568 // Set process to stopped
569 self.state = 'stopped';
570 // Return
571 resolve();
572 });
573
574 // Terminate the process
575 self.process.kill(signal);
576
577 case 5:
578 case 'end':
579 return _context7.stop();
580 }
581 }
582 }, _callee7, this);
583 })).catch(reject);
584 });
585 }
586 }, {
587 key: 'restart',
588 value: function restart(purge) {
589 var self = this;
590
591 return new Promise(function (resolve, reject) {
592 co(regeneratorRuntime.mark(function _callee8() {
593 return regeneratorRuntime.wrap(function _callee8$(_context8) {
594 while (1) {
595 switch (_context8.prev = _context8.next) {
596 case 0:
597 _context8.next = 2;
598 return self.stop();
599
600 case 2:
601 if (!purge) {
602 _context8.next = 5;
603 break;
604 }
605
606 _context8.next = 5;
607 return self.purge();
608
609 case 5:
610 _context8.next = 7;
611 return self.start();
612
613 case 7:
614 resolve();
615
616 case 8:
617 case 'end':
618 return _context8.stop();
619 }
620 }
621 }, _callee8, this);
622 })).catch(reject);
623 });
624 }
625 }, {
626 key: 'config',
627 get: function get() {
628 return clone(this.options);
629 }
630 }, {
631 key: 'host',
632 get: function get() {
633 return this.options.bind_ip;
634 }
635 }, {
636 key: 'port',
637 get: function get() {
638 return this.options.port;
639 }
640 }, {
641 key: 'name',
642 get: function get() {
643 return f('%s:%s', this.options.bind_ip, this.options.port);
644 }
645 }]);
646
647 return Server;
648})();
649
650module.exports = Server;
651
652// SIGHUP 1 Term Hangup detected on controlling terminal
653// or death of controlling process
654// SIGINT 2 Term Interrupt from keyboard
655// SIGQUIT 3 Core Quit from keyboard
656// SIGILL 4 Core Illegal Instruction
657// SIGABRT 6 Core Abort signal from abort(3)
658// SIGFPE 8 Core Floating point exception
659// SIGKILL 9 Term Kill signal
660// SIGSEGV 11 Core Invalid memory reference
661// SIGPIPE 13 Term Broken pipe: write to pipe with no readers
662// SIGALRM 14 Term Timer signal from alarm(2)
663// SIGTERM 15 Term Termination signal
664// Signal map
665var signals = {
666 1: 'SIGHUP',
667 2: 'SIGINT',
668 3: 'SIGQUIT',
669 4: 'SIGABRT',
670 6: 'SIGABRT',
671 8: 'SIGFPE',
672 9: 'SIGKILL',
673 11: 'SIGSEGV',
674 13: 'SIGPIPE',
675 14: 'SIGALRM',
676 15: 'SIGTERM',
677 17: 'SIGSTOP',
678 19: 'SIGSTOP',
679 23: 'SIGSTOP'
680};