Skip to content

Commit 3e17cfe

Browse files
committed
feat(process): improve restart flow
no issue - add optional restart method to process managers that will, by default, just run the stop & start methods of the process manager in sequence. However, process managers can choose to override this with a custom implementation that is more suited to their individual setups - add tests around restart flow
1 parent 09447f6 commit 3e17cfe

File tree

5 files changed

+109
-8
lines changed

5 files changed

+109
-8
lines changed

extensions/systemd/systemd.js

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class SystemdProcessManager extends cli.ProcessManager {
1818
.catch((error) => Promise.reject(new cli.errors.ProcessError(error)));
1919
}
2020

21+
restart() {
22+
return this.ui.sudo(`systemctl restart ${this.systemdName}`)
23+
.catch((error) => Promise.reject(new cli.errors.ProcessError(error)));
24+
}
25+
2126
isRunning() {
2227
try {
2328
execa.shellSync(`systemctl is-active ${this.systemdName}`);

lib/commands/restart.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
'use strict';
22
const Command = require('../command');
3-
const StartCommand = require('./start');
4-
const StopCommand = require('./stop');
53

64
class RestartCommand extends Command {
7-
run(argv) {
5+
run() {
86
let instance = this.system.getInstance();
97

108
if (!instance.running) {
@@ -13,7 +11,7 @@ class RestartCommand extends Command {
1311

1412
instance.loadRunningEnvironment(true);
1513

16-
return this.runCommand(StopCommand, argv).then(() => this.runCommand(StartCommand, argv));
14+
return this.ui.run(instance.process.restart(process.cwd(), this.system.environment), 'Restarting Ghost');
1715
}
1816
}
1917

lib/process-manager.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@ class ProcessManager {
2828
*/
2929
start() {
3030
// Base implementation - noop
31+
return Promise.resolve();
3132
}
3233

3334
stop() {
3435
// Base implementation - noop
36+
return Promise.resolve();
37+
}
38+
39+
restart(cwd, env) {
40+
return this.stop(cwd, env).then(() => this.start(cwd, env));
3541
}
3642

3743
success() {
@@ -60,11 +66,11 @@ class ProcessManager {
6066
}
6167

6268
function isValid(SubClass) {
63-
if (!SubClass.prototype instanceof ProcessManager) {
69+
if (!(SubClass.prototype instanceof ProcessManager)) {
6470
return false;
6571
}
6672

67-
let missing = requiredMethods.filter((method) => !SubClass.prototype[method]);
73+
let missing = requiredMethods.filter((method) => !SubClass.prototype.hasOwnProperty(method));
6874

6975
if (!missing.length) {
7076
return true;
@@ -74,6 +80,4 @@ function isValid(SubClass) {
7480
}
7581

7682
module.exports = ProcessManager
77-
// Rather than make it a static method,
78-
// we export it here so subclasses can't override it
7983
module.exports.isValid = isValid;

test/unit/commands/restart.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
const expect = require('chai').expect;
3+
const sinon = require('sinon');
4+
5+
const RestartCommand = require('../../../lib/commands/restart');
6+
const Instance = require('../../../lib/instance');
7+
8+
describe('Unit: Command > Restart', function () {
9+
it('throws error if instance is not running', function () {
10+
class TestInstance extends Instance {
11+
get running() { return false; }
12+
}
13+
let testInstance = new TestInstance();
14+
15+
let command = new RestartCommand({}, {
16+
getInstance: () => testInstance
17+
});
18+
19+
return command.run().then(() => {
20+
throw new Error('Run method should have thrown');
21+
}).catch((error) => {
22+
expect(error.message).to.match(/instance is not currently running/);
23+
});
24+
});
25+
26+
it('calls process restart method if instance is running', function () {
27+
let restartStub = sinon.stub().resolves();
28+
class TestInstance extends Instance {
29+
get running() { return true; }
30+
get process() { return { restart: restartStub }; }
31+
}
32+
let testInstance = new TestInstance();
33+
let loadRunEnvStub = sinon.stub(testInstance, 'loadRunningEnvironment');
34+
let runStub = sinon.stub().resolves();
35+
36+
let command = new RestartCommand({
37+
run: runStub
38+
}, {
39+
environment: 'testing',
40+
getInstance: () => testInstance
41+
});
42+
43+
return command.run().then(() => {
44+
expect(loadRunEnvStub.calledOnce).to.be.true;
45+
expect(loadRunEnvStub.args[0][0]).to.be.true;
46+
expect(restartStub.calledOnce).to.be.true;
47+
expect(restartStub.args[0]).to.deep.equal([process.cwd(), 'testing']);
48+
expect(runStub.calledOnce).to.be.true;
49+
});
50+
});
51+
});

test/unit/process-manager.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const expect = require('chai').expect;
3+
const sinon = require('sinon');
4+
5+
const ProcessManager = require('../../lib/process-manager');
6+
7+
describe('Unit: Process Manager', function () {
8+
describe('isValid', function () {
9+
it('returns false if passed class is not a subclass of ProcessManager', function () {
10+
let result = ProcessManager.isValid({});
11+
expect(result).to.be.false;
12+
});
13+
14+
it('returns array of missing methods if class is missing required methods', function () {
15+
class TestProcess extends ProcessManager {}
16+
let result = ProcessManager.isValid(TestProcess);
17+
18+
expect(result).to.deep.equal(['start', 'stop', 'isRunning']);
19+
});
20+
21+
it('returns true if class exists and implements all the right methods', function () {
22+
class TestProcess extends ProcessManager {
23+
start() { return Promise.resolve(); }
24+
stop() { return Promise.resolve(); }
25+
isRunning() { return true; }
26+
}
27+
let result = ProcessManager.isValid(TestProcess);
28+
expect(result).to.be.true;
29+
});
30+
});
31+
32+
it('restart base implementation calls start and stop methods', function () {
33+
let instance = new ProcessManager({}, {}, {});
34+
let startStub = sinon.stub(instance, 'start').resolves();
35+
let stopStub = sinon.stub(instance, 'stop').resolves();
36+
37+
return instance.restart().then(() => {
38+
expect(stopStub.calledOnce).to.be.true;
39+
expect(startStub.calledOnce).to.be.true;
40+
expect(stopStub.calledBefore(startStub)).to.be.true;
41+
})
42+
});
43+
});

0 commit comments

Comments
 (0)