UNPKG

46.8 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 Server = require('./server'),
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 waitMS = function waitMS(ms) {
22 return new Promise(function (resolve, reject) {
23 setTimeout(function () {
24 resolve();
25 }, ms);
26 });
27};
28
29var ConfigServers = (function () {
30 function ConfigServers(binary, nodes, options) {
31 _classCallCheck(this, ConfigServers);
32
33 options = options || {};
34 // Save the default passed in parameters
35 this.nodes = nodes;
36 this.options = clone(options);
37
38 // Ensure we have a list of nodes
39 if (!Array.isArray(this.nodes) || this.nodes.length == 0) {
40 throw new Error('a list of nodes must be passed in');
41 }
42
43 // Server state
44 this.state = 'stopped';
45
46 // Unpack default runtime information
47 this.binary = binary || 'mongod';
48
49 // Wait times
50 this.electionCycleWaitMS = typeof this.options.electionCycleWaitMS == 'number' ? this.options.electionCycleWaitMS : 31000;
51 this.retryWaitMS = typeof this.options.retryWaitMS == 'number' ? this.options.retryWaitMS : 5000;
52
53 // Remove the values from the options
54 delete this.options['electionCycleWaitMS'];
55 delete this.options['retryWaitMS'];
56
57 // Self reference
58 var self = this;
59
60 // Create server managers for each node
61 this.managers = this.nodes.map(function (x) {
62 var opts = clone(x);
63 delete opts['logpath'];
64 delete opts['replSet'];
65
66 // Add the needed config server options
67 if (!opts.configsvr) opts.configsvr = null;
68
69 // Set server instance
70 var server = new Server(self.binary, opts, options);
71
72 // Create manager
73 return server;
74 });
75 }
76
77 _createClass(ConfigServers, [{
78 key: 'discover',
79 value: function discover() {
80 var self = this;
81
82 return new Promise(function (resolve, reject) {
83 co(regeneratorRuntime.mark(function _callee() {
84 var proc, stdout, stderr;
85 return regeneratorRuntime.wrap(function _callee$(_context) {
86 while (1) {
87 switch (_context.prev = _context.next) {
88 case 0:
89 proc = spawn(self.binary, ['--version']);
90 // Variables receiving data
91
92 stdout = '';
93 stderr = '';
94 // Get the stdout
95
96 proc.stdout.on('data', function (data) {
97 stdout += data;
98 });
99 // Get the stderr
100 proc.stderr.on('data', function (data) {
101 stderr += data;
102 });
103 // Got an error
104 proc.on('error', function (err) {
105 reject(err);
106 });
107 // Process terminated
108 proc.on('close', function (code) {
109 // Perform version match
110 var versionMatch = stdout.match(/[0-9]+\.[0-9]+\.[0-9]+/);
111
112 // Check if we have ssl
113 var sslMatch = stdout.match(/ssl/i);
114
115 // Resolve the server version
116 resolve({
117 version: versionMatch.toString().split('.').map(function (x) {
118 return parseInt(x, 10);
119 }),
120 ssl: sslMatch != null
121 });
122 });
123
124 case 7:
125 case 'end':
126 return _context.stop();
127 }
128 }
129 }, _callee, this);
130 })).catch(reject);
131 });
132 }
133 }, {
134 key: 'start',
135 value: function start() {
136 var self = this;
137
138 return new Promise(function (resolve, reject) {
139 co(regeneratorRuntime.mark(function _callee2() {
140 var i;
141 return regeneratorRuntime.wrap(function _callee2$(_context2) {
142 while (1) {
143 switch (_context2.prev = _context2.next) {
144 case 0:
145 if (!(self.state == 'running')) {
146 _context2.next = 2;
147 break;
148 }
149
150 return _context2.abrupt('return', resolve());
151
152 case 2:
153 i = 0;
154
155 case 3:
156 if (!(i < self.managers.length)) {
157 _context2.next = 9;
158 break;
159 }
160
161 _context2.next = 6;
162 return self.managers[i].start();
163
164 case 6:
165 i++;
166 _context2.next = 3;
167 break;
168
169 case 9:
170
171 // Set the state to running
172 self.state == 'running';
173
174 // We have a stable replicaset
175 resolve();
176
177 case 11:
178 case 'end':
179 return _context2.stop();
180 }
181 }
182 }, _callee2, this);
183 })).catch(reject);
184 });
185 }
186
187 /**
188 * Return members url
189 * @method
190 * return {String}
191 */
192
193 }, {
194 key: 'url',
195 value: function url() {
196 var members = this.nodes.map(function (x) {
197 return f('%s:%s', x.bind_ip || 'localhost', x.port);
198 });
199
200 // Generate the url
201 return f('%s', members.join(','));
202 }
203
204 /**
205 * Locate all the arbiters
206 * @method
207 * @returns {Promise}
208 */
209
210 }, {
211 key: 'secondaries',
212 value: function secondaries() {
213 var self = this;
214
215 return new Promise(function (resolve, reject) {
216 co(regeneratorRuntime.mark(function _callee3() {
217 var arbiters, i, ismaster;
218 return regeneratorRuntime.wrap(function _callee3$(_context3) {
219 while (1) {
220 switch (_context3.prev = _context3.next) {
221 case 0:
222 arbiters = [];
223
224 // Go over all the managers
225
226 i = 0;
227
228 case 2:
229 if (!(i < self.managers.length)) {
230 _context3.next = 10;
231 break;
232 }
233
234 _context3.next = 5;
235 return self.managers[i].ismaster();
236
237 case 5:
238 ismaster = _context3.sent;
239
240 if (ismaster.arbiterOnly) arbiters.push(self.managers[i]);
241
242 case 7:
243 i++;
244 _context3.next = 2;
245 break;
246
247 case 10:
248
249 resolve(arbiters);
250
251 case 11:
252 case 'end':
253 return _context3.stop();
254 }
255 }
256 }, _callee3, this);
257 })).catch(reject);
258 });
259 }
260
261 /**
262 * Locate all the secondaries
263 * @method
264 * @returns {Promise}
265 */
266
267 }, {
268 key: 'secondaries',
269 value: function secondaries() {
270 var self = this;
271
272 return new Promise(function (resolve, reject) {
273 co(regeneratorRuntime.mark(function _callee4() {
274 var secondaries, i, ismaster;
275 return regeneratorRuntime.wrap(function _callee4$(_context4) {
276 while (1) {
277 switch (_context4.prev = _context4.next) {
278 case 0:
279 secondaries = [];
280
281 // Go over all the managers
282
283 i = 0;
284
285 case 2:
286 if (!(i < self.managers.length)) {
287 _context4.next = 10;
288 break;
289 }
290
291 _context4.next = 5;
292 return self.managers[i].ismaster();
293
294 case 5:
295 ismaster = _context4.sent;
296
297 if (ismaster.secondary) secondaries.push(self.managers[i]);
298
299 case 7:
300 i++;
301 _context4.next = 2;
302 break;
303
304 case 10:
305
306 resolve(secondaries);
307
308 case 11:
309 case 'end':
310 return _context4.stop();
311 }
312 }
313 }, _callee4, this);
314 })).catch(reject);
315 });
316 }
317
318 /**
319 * Block until we have a new primary available
320 * @method
321 * @returns {Promise}
322 */
323
324 }, {
325 key: 'waitForPrimary',
326 value: function waitForPrimary() {
327 var self = this;
328 var waitedForElectionCycle = false;
329
330 return new Promise(function (resolve, reject) {
331 co(regeneratorRuntime.mark(function _callee5() {
332 var i, ismaster;
333 return regeneratorRuntime.wrap(function _callee5$(_context5) {
334 while (1) {
335 switch (_context5.prev = _context5.next) {
336 case 0:
337 if (!true) {
338 _context5.next = 34;
339 break;
340 }
341
342 i = 0;
343
344 case 2:
345 if (!(i < self.managers.length)) {
346 _context5.next = 30;
347 break;
348 }
349
350 _context5.prev = 3;
351 _context5.next = 6;
352 return self.managers[i].ismaster();
353
354 case 6:
355 ismaster = _context5.sent;
356
357 if (!(ismaster.electionId && ismaster.ismaster && !ismaster.electionId.equals(self.electionId))) {
358 _context5.next = 13;
359 break;
360 }
361
362 // We have a new primary
363 self.electionId = ismaster.electionId;
364 self.lastKnownPrimary = ismaster.me;
365 // Return the manager
366 return _context5.abrupt('return', resolve(self.managers[i]));
367
368 case 13:
369 if (!(ismaster.ismaster && !waitedForElectionCycle)) {
370 _context5.next = 19;
371 break;
372 }
373
374 _context5.next = 16;
375 return waitMS(self.electionCycleWaitMS);
376
377 case 16:
378 // Set waitedForElectionCycle
379 waitedForElectionCycle = true;
380 _context5.next = 21;
381 break;
382
383 case 19:
384 if (!(ismaster.ismaster && waitedForElectionCycle)) {
385 _context5.next = 21;
386 break;
387 }
388
389 return _context5.abrupt('return', resolve());
390
391 case 21:
392 _context5.next = 27;
393 break;
394
395 case 23:
396 _context5.prev = 23;
397 _context5.t0 = _context5['catch'](3);
398 _context5.next = 27;
399 return waitMS(self.retryWaitMS);
400
401 case 27:
402 i++;
403 _context5.next = 2;
404 break;
405
406 case 30:
407 _context5.next = 32;
408 return waitMS(1000);
409
410 case 32:
411 _context5.next = 0;
412 break;
413
414 case 34:
415 case 'end':
416 return _context5.stop();
417 }
418 }
419 }, _callee5, this, [[3, 23]]);
420 })).catch(reject);
421 });
422 }
423
424 /**
425 * Step down the primary server
426 * @method
427 * @param {boolean} [returnImmediately=false] Return immediately after executing stepdown, otherwise block until new primary is available.
428 * @param {number} [options.stepDownSecs=60] The number of seconds to wait before stepping down primary.
429 * @param {number} [options.secondaryCatchUpPeriodSecs=null] The number of seconds that the mongod will wait for an electable secondary to catch up to the primary.
430 * @param {boolean} [options.force=false] A boolean that determines whether the primary steps down if no electable and up-to-date secondary exists within the wait period.
431 * @returns {Promise}
432 */
433
434 }, {
435 key: 'stepDownPrimary',
436 value: function stepDownPrimary(returnImmediately, options, credentials) {
437 var self = this;
438 options = options || {};
439
440 return new Promise(function (resolve, reject) {
441 co(regeneratorRuntime.mark(function _callee6() {
442 var command, name, manager, result;
443 return regeneratorRuntime.wrap(function _callee6$(_context6) {
444 while (1) {
445 switch (_context6.prev = _context6.next) {
446 case 0:
447 options = clone(options);
448
449 // Step down command
450 command = {
451 replSetStepDown: typeof options.stepDownSecs == 'number' ? options.stepDownSecs : 60
452 };
453
454 // Remove stepDownSecs
455
456 delete options['stepDownSecs'];
457 // Mix in any other options
458 for (name in options) {
459 command[name] = options[name];
460 }
461
462 // Locate the current primary
463 _context6.next = 6;
464 return self.primary();
465
466 case 6:
467 manager = _context6.sent;
468
469 if (!(manager == null)) {
470 _context6.next = 9;
471 break;
472 }
473
474 return _context6.abrupt('return', reject(new Error('no primary found in the replicaset')));
475
476 case 9:
477 _context6.prev = 9;
478 _context6.next = 12;
479 return manager.executeCommand('admin.$cmd', command, credentials);
480
481 case 12:
482 result = _context6.sent;
483 _context6.next = 19;
484 break;
485
486 case 15:
487 _context6.prev = 15;
488 _context6.t0 = _context6['catch'](9);
489
490 if (!(_context6.t0.ok == 0)) {
491 _context6.next = 19;
492 break;
493 }
494
495 return _context6.abrupt('return', reject(new Error('failed to step down primary')));
496
497 case 19:
498 if (!returnImmediately) {
499 _context6.next = 21;
500 break;
501 }
502
503 return _context6.abrupt('return', resolve());
504
505 case 21:
506 _context6.next = 23;
507 return self.waitForPrimary();
508
509 case 23:
510
511 // Finish up
512 resolve();
513
514 case 24:
515 case 'end':
516 return _context6.stop();
517 }
518 }
519 }, _callee6, this, [[9, 15]]);
520 })).catch(reject);
521 });
522 }
523
524 /**
525 * Get the current replicaset configuration
526 * @method
527 * @param {object} manager The server manager that we wish to remove from the set.
528 * @param {object} [credentials] Credentials needed to perform an admin authenticated command.
529 * @returns {Promise}
530 */
531
532 }, {
533 key: 'configuration',
534 value: function configuration(manager, credentials) {
535 return new Promise(function (resolve, reject) {
536 co(regeneratorRuntime.mark(function _callee7() {
537 var result;
538 return regeneratorRuntime.wrap(function _callee7$(_context7) {
539 while (1) {
540 switch (_context7.prev = _context7.next) {
541 case 0:
542 _context7.next = 2;
543 return manager.executeCommand('admin.$cmd', {
544 replSetGetConfig: true
545 }, credentials);
546
547 case 2:
548 result = _context7.sent;
549
550 if (!(result && result.ok == 0)) {
551 _context7.next = 5;
552 break;
553 }
554
555 return _context7.abrupt('return', reject(new Error(f('failed to execute replSetGetConfig against server [%s]', node.name))));
556
557 case 5:
558
559 resolve(result.config);
560
561 case 6:
562 case 'end':
563 return _context7.stop();
564 }
565 }
566 }, _callee7, this);
567 })).catch(reject);
568 });
569 }
570
571 /**
572 * Set a new configuration
573 * @method
574 * @param {object} configuration The configuration JSON object
575 * @param {object} [options] Any options for the operation.
576 * @param {boolean} [options.returnImmediately=false] Return immediately after executing stepdown, otherwise block until new primary is available.
577 * @param {boolean} [options.force=false] Force the server reconfiguration
578 * @param {object} [credentials] Credentials needed to perform an admin authenticated command.
579 * @returns {Promise}
580 */
581
582 }, {
583 key: 'reconfigure',
584 value: function reconfigure(config, options, credentials) {
585 options = options || { returnImmediately: false };
586 var self = this;
587
588 // Default returnImmediately to false
589 var returnImmediately = typeof options.returnImmediately == 'boolean' ? options.returnImmediately : false;
590 // Default force to false
591 var force = typeof options.force == 'boolean' ? options.force : false;
592
593 return new Promise(function (resolve, reject) {
594 co(regeneratorRuntime.mark(function _callee8() {
595 var lastConfig, primary, result, waitedForElectionCycle, ismaster;
596 return regeneratorRuntime.wrap(function _callee8$(_context8) {
597 while (1) {
598 switch (_context8.prev = _context8.next) {
599 case 0:
600 // Last known config
601 lastConfig = self.configurations[self.configurations.length - 1];
602 // Grab the current configuration and clone it (including member object)
603
604 config = clone(config);
605 config.members = config.members.map(function (x) {
606 return clone(x);
607 });
608
609 // Update the version to the latest + 1
610 config.version = lastConfig.version + 1;
611
612 // Reconfigure the replicaset
613 _context8.next = 6;
614 return self.primary();
615
616 case 6:
617 primary = _context8.sent;
618
619 if (primary) {
620 _context8.next = 9;
621 break;
622 }
623
624 return _context8.abrupt('return', reject(new Error('no primary available')));
625
626 case 9:
627 _context8.next = 11;
628 return primary.executeCommand('admin.$cmd', {
629 replSetReconfig: config, force: force
630 }, credentials);
631
632 case 11:
633 result = _context8.sent;
634
635 if (!(result && result.ok == 0)) {
636 _context8.next = 14;
637 break;
638 }
639
640 return _context8.abrupt('return', reject(new Error(f('failed to execute replSetReconfig with configuration [%s]', JSON.stringify(config)))));
641
642 case 14:
643
644 // Push new configuration to list
645 self.configurations.push(config);
646
647 // If we want to return immediately do so now
648
649 if (!returnImmediately) {
650 _context8.next = 17;
651 break;
652 }
653
654 return _context8.abrupt('return', resolve(server));
655
656 case 17:
657
658 // Found a valid state
659 waitedForElectionCycle = false;
660
661 // Wait for the server to get in a stable state
662
663 case 18:
664 if (!true) {
665 _context8.next = 60;
666 break;
667 }
668
669 _context8.prev = 19;
670 _context8.next = 22;
671 return self.primary();
672
673 case 22:
674 primary = _context8.sent;
675
676 if (primary) {
677 _context8.next = 27;
678 break;
679 }
680
681 _context8.next = 26;
682 return waitMS(self.retryWaitMS);
683
684 case 26:
685 return _context8.abrupt('continue', 18);
686
687 case 27:
688 _context8.next = 29;
689 return primary.ismaster();
690
691 case 29:
692 ismaster = _context8.sent;
693
694 if (!(ismaster.ismaster && ismaster.electionId && !self.electionId.equals(ismaster.electionId))) {
695 _context8.next = 36;
696 break;
697 }
698
699 _context8.next = 33;
700 return self.waitForPrimary();
701
702 case 33:
703 return _context8.abrupt('return', resolve());
704
705 case 36:
706 if (!((ismaster.secondary || ismaster.arbiterOnly) && ismaster.electionId && self.electionId.equals(ismaster.electionId))) {
707 _context8.next = 40;
708 break;
709 }
710
711 return _context8.abrupt('return', resolve());
712
713 case 40:
714 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && !waitedForElectionCycle)) {
715 _context8.next = 46;
716 break;
717 }
718
719 // Wait for an election cycle to have passed
720 waitedForElectionCycle = true;
721 _context8.next = 44;
722 return waitMS(self.electionCycleWaitMS);
723
724 case 44:
725 _context8.next = 52;
726 break;
727
728 case 46:
729 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && waitedForElectionCycle)) {
730 _context8.next = 50;
731 break;
732 }
733
734 return _context8.abrupt('return', resolve());
735
736 case 50:
737 _context8.next = 52;
738 return waitMS(self.retryWaitMS);
739
740 case 52:
741 _context8.next = 58;
742 break;
743
744 case 54:
745 _context8.prev = 54;
746 _context8.t0 = _context8['catch'](19);
747 _context8.next = 58;
748 return waitMS(self.retryWaitMS);
749
750 case 58:
751 _context8.next = 18;
752 break;
753
754 case 60:
755
756 // Should not reach here
757 reject(new Error(f('failed to successfully set a configuration [%s]', JSON.stringify(config))));
758
759 case 61:
760 case 'end':
761 return _context8.stop();
762 }
763 }
764 }, _callee8, this, [[19, 54]]);
765 })).catch(reject);
766 });
767 }
768
769 /**
770 * Adds a new member to the replicaset
771 * @method
772 * @param {object} node All the settings used to boot the mongod process.
773 * @param {object} [options] Any options for the operation.
774 * @param {boolean} [options.returnImmediately=false] Return immediately after executing stepdown, otherwise block until new primary is available.
775 * @param {boolean} [options.force=false] Force the server reconfiguration
776 * @param {object} [credentials] Credentials needed to perform an admin authenticated command.
777 * @returns {Promise}
778 */
779
780 }, {
781 key: 'addMember',
782 value: function addMember(node, options, credentials) {
783 options = options || { returnImmediately: false };
784 var self = this;
785
786 // Default returnImmediately to false
787 var returnImmediately = typeof options.returnImmediately == 'boolean' ? options.returnImmediately : false;
788 // Default force to false
789 var force = typeof options.force == 'boolean' ? options.force : false;
790
791 return new Promise(function (resolve, reject) {
792 co(regeneratorRuntime.mark(function _callee9() {
793 var opts, server, max, config, member, primary, result, waitedForElectionCycle, ismaster;
794 return regeneratorRuntime.wrap(function _callee9$(_context9) {
795 while (1) {
796 switch (_context9.prev = _context9.next) {
797 case 0:
798 // Clone the top level settings
799 node = clone(node);
800 // Clone the settings and remove the logpath
801 opts = clone(node.options);
802
803 delete opts['logpath'];
804
805 // Add the needed replicaset options
806 opts.replSet = self.options.replSet;
807
808 // Create a new server instance
809 server = new Server(self.binary, opts, self.options);
810
811 // Purge the directory
812
813 _context9.next = 7;
814 return server.purge();
815
816 case 7:
817 _context9.next = 9;
818 return server.start();
819
820 case 9:
821 if (!(self.configurations.length == 0)) {
822 _context9.next = 11;
823 break;
824 }
825
826 return _context9.abrupt('return', reject(new Error('no configurations exist yet, did you start the replicaset?')));
827
828 case 11:
829
830 // Locate max id
831 max = 0;
832
833 // Grab the current configuration and clone it (including member object)
834
835 config = clone(self.configurations[self.configurations.length - 1]);
836
837 config.members = config.members.map(function (x) {
838 max = x._id > max ? x._id : max;
839 return clone(x);
840 });
841
842 // Let's add our new server to the configuration
843 delete node['options'];
844 // Create the member
845 member = {
846 _id: max + 1,
847 host: f('%s:%s', opts.bind_ip, opts.port)
848 };
849
850 // Did we specify any special options
851
852 if (node.arbiter) member.arbiterOnly = true;
853 if (node.builIndexes) member.buildIndexes = true;
854 if (node.hidden) member.hidden = true;
855 if (typeof node.priority == 'number') member.priority = node.priority;
856 if (node.tags) member.tags = node.tags;
857 if (node.slaveDelay) member.slaveDelay = node.slaveDelay;
858 if (node.votes) member.votes = node.votes;
859
860 // Add to the list of members
861 config.members.push(member);
862 // Update the configuration version
863 config.version = config.version + 1;
864
865 // Reconfigure the replicaset
866 _context9.next = 27;
867 return self.primary();
868
869 case 27:
870 primary = _context9.sent;
871
872 if (primary) {
873 _context9.next = 30;
874 break;
875 }
876
877 return _context9.abrupt('return', reject(new Error('no primary available')));
878
879 case 30:
880 _context9.next = 32;
881 return primary.executeCommand('admin.$cmd', {
882 replSetReconfig: config, force: force
883 }, credentials);
884
885 case 32:
886 result = _context9.sent;
887
888 if (!(result && result.ok == 0)) {
889 _context9.next = 35;
890 break;
891 }
892
893 return _context9.abrupt('return', reject(new Error(f('failed to execute replSetReconfig with configuration [%s]', JSON.stringify(config)))));
894
895 case 35:
896
897 // Push new configuration to list
898 self.configurations.push(config);
899
900 // Add manager to list of managers
901 self.managers.push(server);
902
903 // If we want to return immediately do so now
904
905 if (!returnImmediately) {
906 _context9.next = 39;
907 break;
908 }
909
910 return _context9.abrupt('return', resolve(server));
911
912 case 39:
913
914 // Found a valid state
915 waitedForElectionCycle = false;
916
917 // Wait for the server to get in a stable state
918
919 case 40:
920 if (!true) {
921 _context9.next = 75;
922 break;
923 }
924
925 _context9.prev = 41;
926 _context9.next = 44;
927 return server.ismaster();
928
929 case 44:
930 ismaster = _context9.sent;
931
932 if (!(ismaster.ismaster && ismaster.electionId && !self.electionId.equals(ismaster.electionId))) {
933 _context9.next = 51;
934 break;
935 }
936
937 _context9.next = 48;
938 return self.waitForPrimary();
939
940 case 48:
941 return _context9.abrupt('return', resolve(server));
942
943 case 51:
944 if (!((ismaster.secondary || ismaster.arbiterOnly) && ismaster.electionId && self.electionId.equals(ismaster.electionId))) {
945 _context9.next = 55;
946 break;
947 }
948
949 return _context9.abrupt('return', resolve(server));
950
951 case 55:
952 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && !waitedForElectionCycle)) {
953 _context9.next = 61;
954 break;
955 }
956
957 // Wait for an election cycle to have passed
958 waitedForElectionCycle = true;
959 _context9.next = 59;
960 return waitMS(self.electionCycleWaitMS);
961
962 case 59:
963 _context9.next = 67;
964 break;
965
966 case 61:
967 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && waitedForElectionCycle)) {
968 _context9.next = 65;
969 break;
970 }
971
972 return _context9.abrupt('return', resolve(server));
973
974 case 65:
975 _context9.next = 67;
976 return waitMS(self.retryWaitMS);
977
978 case 67:
979 _context9.next = 73;
980 break;
981
982 case 69:
983 _context9.prev = 69;
984 _context9.t0 = _context9['catch'](41);
985 _context9.next = 73;
986 return waitMS(self.retryWaitMS);
987
988 case 73:
989 _context9.next = 40;
990 break;
991
992 case 75:
993
994 // Should not reach here
995 reject(new Error(f('failed to successfully add a new member with options [%s]', JSON.stringify(node))));
996
997 case 76:
998 case 'end':
999 return _context9.stop();
1000 }
1001 }
1002 }, _callee9, this, [[41, 69]]);
1003 })).catch(reject);
1004 });
1005 }
1006
1007 /**
1008 * Remove a member from the set
1009 * @method
1010 * @param {object} manager The server manager that we wish to remove from the set.
1011 * @param {object} [options] Any options for the operation.
1012 * @param {boolean} [options.returnImmediately=false] Return immediately after executing stepdown, otherwise block until new primary is available.
1013 * @param {boolean} [options.force=false] Force the server reconfiguration
1014 * @param {object} [credentials] Credentials needed to perform an admin authenticated command.
1015 * @returns {Promise}
1016 */
1017
1018 }, {
1019 key: 'removeMember',
1020 value: function removeMember(node, options, credentials) {
1021 options = options || { returnImmediately: false };
1022 var self = this;
1023
1024 // Default returnImmediately to false
1025 var returnImmediately = typeof options.returnImmediately == 'boolean' ? options.returnImmediately : false;
1026 // Default force to false
1027 var force = typeof options.force == 'boolean' ? options.force : false;
1028
1029 return new Promise(function (resolve, reject) {
1030 co(regeneratorRuntime.mark(function _callee10() {
1031 var config, primary, result, waitedForElectionCycle, ismaster;
1032 return regeneratorRuntime.wrap(function _callee10$(_context10) {
1033 while (1) {
1034 switch (_context10.prev = _context10.next) {
1035 case 0:
1036 // Grab the current configuration and clone it (including member object)
1037 config = clone(self.configurations[self.configurations.length - 1]);
1038
1039 config.members = config.members.map(function (x) {
1040 return clone(x);
1041 });
1042
1043 // Locate the member and remove it
1044 config.members = config.members.filter(function (x) {
1045 return x.host != node.name;
1046 });
1047 // Update the configuration version
1048 config.version = config.version + 1;
1049
1050 // Reconfigure the replicaset
1051 _context10.next = 6;
1052 return self.primary();
1053
1054 case 6:
1055 primary = _context10.sent;
1056
1057 if (primary) {
1058 _context10.next = 9;
1059 break;
1060 }
1061
1062 return _context10.abrupt('return', reject(new Error('no primary available')));
1063
1064 case 9:
1065 _context10.next = 11;
1066 return primary.executeCommand('admin.$cmd', {
1067 replSetReconfig: config, force: force
1068 }, credentials);
1069
1070 case 11:
1071 result = _context10.sent;
1072
1073 if (!(result && result.ok == 0)) {
1074 _context10.next = 14;
1075 break;
1076 }
1077
1078 return _context10.abrupt('return', reject(new Error(f('failed to execute replSetReconfig with configuration [%s]', JSON.stringify(config)))));
1079
1080 case 14:
1081
1082 // Push new configuration to list
1083 self.configurations.push(config);
1084
1085 // Remove from the list of managers
1086 self.managers = self.managers.filter(function (x) {
1087 return x.name != node.name;
1088 });
1089
1090 // If we want to return immediately do so now
1091
1092 if (!returnImmediately) {
1093 _context10.next = 20;
1094 break;
1095 }
1096
1097 _context10.next = 19;
1098 return node.stop();
1099
1100 case 19:
1101 return _context10.abrupt('return', resolve());
1102
1103 case 20:
1104
1105 // Found a valid state
1106 waitedForElectionCycle = false;
1107
1108 // Wait for the server to get in a stable state
1109
1110 case 21:
1111 if (!true) {
1112 _context10.next = 67;
1113 break;
1114 }
1115
1116 _context10.prev = 22;
1117 _context10.next = 25;
1118 return self.primary();
1119
1120 case 25:
1121 primary = _context10.sent;
1122
1123 if (primary) {
1124 _context10.next = 30;
1125 break;
1126 }
1127
1128 _context10.next = 29;
1129 return waitMS(self.retryWaitMS);
1130
1131 case 29:
1132 return _context10.abrupt('continue', 21);
1133
1134 case 30:
1135 _context10.next = 32;
1136 return primary.ismaster();
1137
1138 case 32:
1139 ismaster = _context10.sent;
1140
1141 if (!(ismaster.ismaster && ismaster.electionId && !self.electionId.equals(ismaster.electionId))) {
1142 _context10.next = 41;
1143 break;
1144 }
1145
1146 _context10.next = 36;
1147 return self.waitForPrimary();
1148
1149 case 36:
1150 _context10.next = 38;
1151 return node.stop();
1152
1153 case 38:
1154 return _context10.abrupt('return', resolve());
1155
1156 case 41:
1157 if (!((ismaster.secondary || ismaster.arbiterOnly) && ismaster.electionId && self.electionId.equals(ismaster.electionId))) {
1158 _context10.next = 45;
1159 break;
1160 }
1161
1162 return _context10.abrupt('return', resolve());
1163
1164 case 45:
1165 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && !waitedForElectionCycle)) {
1166 _context10.next = 51;
1167 break;
1168 }
1169
1170 // Wait for an election cycle to have passed
1171 waitedForElectionCycle = true;
1172 _context10.next = 49;
1173 return waitMS(self.electionCycleWaitMS);
1174
1175 case 49:
1176 _context10.next = 59;
1177 break;
1178
1179 case 51:
1180 if (!((ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly) && waitedForElectionCycle)) {
1181 _context10.next = 57;
1182 break;
1183 }
1184
1185 _context10.next = 54;
1186 return node.stop();
1187
1188 case 54:
1189 return _context10.abrupt('return', resolve());
1190
1191 case 57:
1192 _context10.next = 59;
1193 return waitMS(self.retryWaitMS);
1194
1195 case 59:
1196 _context10.next = 65;
1197 break;
1198
1199 case 61:
1200 _context10.prev = 61;
1201 _context10.t0 = _context10['catch'](22);
1202 _context10.next = 65;
1203 return waitMS(self.retryWaitMS);
1204
1205 case 65:
1206 _context10.next = 21;
1207 break;
1208
1209 case 67:
1210
1211 // Should not reach here
1212 reject(new Error(f('failed to successfully remove member [%s]', JSON.stringify(node.name))));
1213
1214 case 68:
1215 case 'end':
1216 return _context10.stop();
1217 }
1218 }
1219 }, _callee10, this, [[22, 61]]);
1220 }));
1221 });
1222 }
1223
1224 /**
1225 * Remove a member from the set
1226 * @method
1227 * @param {object} node The server manager that we wish to remove from the set.
1228 * @param {object} [options] Any options for the operation.
1229 * @param {boolean} [options.returnImmediately=false] Return immediately after executing stepdown, otherwise block until new primary is available.
1230 * @param {boolean} [options.maxRetries=30] Number of retries before giving up for the server to come back as secondary.
1231 * @param {object} [credentials] Credentials needed to perform an admin authenticated command.
1232 * @returns {Promise}
1233 */
1234
1235 }, {
1236 key: 'maintenance',
1237 value: function maintenance(value, node, options, credentials) {
1238 options = options || { returnImmediately: false };
1239 var self = this;
1240
1241 // Default returnImmediately to false
1242 var returnImmediately = typeof options.returnImmediately == 'boolean' ? options.returnImmediately : false;
1243 var maxRetries = typeof options.maxRetries == 'number' ? options.maxRetries : 30;
1244
1245 return new Promise(function (resolve, reject) {
1246 co(regeneratorRuntime.mark(function _callee11() {
1247 var ismaster, result, currentTries;
1248 return regeneratorRuntime.wrap(function _callee11$(_context11) {
1249 while (1) {
1250 switch (_context11.prev = _context11.next) {
1251 case 0:
1252 _context11.next = 2;
1253 return node.ismaster();
1254
1255 case 2:
1256 ismaster = _context11.sent;
1257
1258 if (!(value == true && !ismaster.secondary)) {
1259 _context11.next = 7;
1260 break;
1261 }
1262
1263 return _context11.abrupt('return', reject(new Error(f('the server at %s is not a secondary', node.name))));
1264
1265 case 7:
1266 if (!(value == false && (ismaster.ismaster || ismaster.secondary || ismaster.arbiterOnly))) {
1267 _context11.next = 9;
1268 break;
1269 }
1270
1271 return _context11.abrupt('return', reject(new Error(f('the server at %s is not in maintenance mode', node.name))));
1272
1273 case 9:
1274 _context11.next = 11;
1275 return node.executeCommand('admin.$cmd', {
1276 replSetMaintenance: value
1277 }, credentials);
1278
1279 case 11:
1280 result = _context11.sent;
1281
1282 if (!(result && result.ok == 0)) {
1283 _context11.next = 14;
1284 break;
1285 }
1286
1287 return _context11.abrupt('return', reject(new Error(f('failed to execute replSetMaintenance for server [%s]', node.name))));
1288
1289 case 14:
1290 if (!(value == false && returnImmediately || value == true)) {
1291 _context11.next = 16;
1292 break;
1293 }
1294
1295 return _context11.abrupt('return', resolve());
1296
1297 case 16:
1298
1299 // Max waitTime
1300 currentTries = maxRetries;
1301
1302 // Did we pull the server back from maintenance mode
1303
1304 case 17:
1305 if (!true) {
1306 _context11.next = 30;
1307 break;
1308 }
1309
1310 if (!(currentTries == 0)) {
1311 _context11.next = 20;
1312 break;
1313 }
1314
1315 return _context11.abrupt('return', reject(new Error(f('server %s failed to come back as a secondary after %s milliseconds waiting', node.name, maxRetries * 1000))));
1316
1317 case 20:
1318 _context11.next = 22;
1319 return waitMS(1000);
1320
1321 case 22:
1322 _context11.next = 24;
1323 return node.ismaster();
1324
1325 case 24:
1326 ismaster = _context11.sent;
1327
1328 if (!ismaster.secondary) {
1329 _context11.next = 27;
1330 break;
1331 }
1332
1333 return _context11.abrupt('return', resolve());
1334
1335 case 27:
1336
1337 currentTries = currentTries - 1;
1338 _context11.next = 17;
1339 break;
1340
1341 case 30:
1342
1343 resolve();
1344
1345 case 31:
1346 case 'end':
1347 return _context11.stop();
1348 }
1349 }
1350 }, _callee11, this);
1351 })).catch(reject);
1352 });
1353 }
1354 }, {
1355 key: 'stop',
1356 value: function stop() {
1357 var self = this;
1358
1359 return new Promise(function (resolve, reject) {
1360 co(regeneratorRuntime.mark(function _callee12() {
1361 var i;
1362 return regeneratorRuntime.wrap(function _callee12$(_context12) {
1363 while (1) {
1364 switch (_context12.prev = _context12.next) {
1365 case 0:
1366 i = 0;
1367
1368 case 1:
1369 if (!(i < self.managers.length)) {
1370 _context12.next = 7;
1371 break;
1372 }
1373
1374 _context12.next = 4;
1375 return self.managers[i].stop();
1376
1377 case 4:
1378 i++;
1379 _context12.next = 1;
1380 break;
1381
1382 case 7:
1383
1384 resolve();
1385
1386 case 8:
1387 case 'end':
1388 return _context12.stop();
1389 }
1390 }
1391 }, _callee12, this);
1392 })).catch(reject);
1393 });
1394 }
1395 }, {
1396 key: 'restart',
1397 value: function restart() {
1398 var self = this;
1399
1400 return new Promise(function (resolve, reject) {
1401 co(regeneratorRuntime.mark(function _callee13() {
1402 return regeneratorRuntime.wrap(function _callee13$(_context13) {
1403 while (1) {
1404 switch (_context13.prev = _context13.next) {
1405 case 0:
1406 case 'end':
1407 return _context13.stop();
1408 }
1409 }
1410 }, _callee13, this);
1411 })).catch(reject);
1412 });
1413 }
1414 }, {
1415 key: 'purge',
1416 value: function purge() {
1417 var self = this;
1418
1419 return new Promise(function (resolve, reject) {
1420 co(regeneratorRuntime.mark(function _callee14() {
1421 var i;
1422 return regeneratorRuntime.wrap(function _callee14$(_context14) {
1423 while (1) {
1424 switch (_context14.prev = _context14.next) {
1425 case 0:
1426 i = 0;
1427
1428 case 1:
1429 if (!(i < self.managers.length)) {
1430 _context14.next = 7;
1431 break;
1432 }
1433
1434 _context14.next = 4;
1435 return self.managers[i].purge();
1436
1437 case 4:
1438 i++;
1439 _context14.next = 1;
1440 break;
1441
1442 case 7:
1443
1444 resolve();
1445
1446 case 8:
1447 case 'end':
1448 return _context14.stop();
1449 }
1450 }
1451 }, _callee14, this);
1452 })).catch(reject);
1453 });
1454 }
1455 }]);
1456
1457 return ConfigServers;
1458})();
1459
1460module.exports = ConfigServers;