Skip to content

Commit

Permalink
feat(core): Ensure that Ghost was started
Browse files Browse the repository at this point in the history
closes TryGhost#472

- has no tests yet
- PR for last dicussions

[ci skip]
  • Loading branch information
kirrg001 committed Feb 3, 2018
1 parent 59f5d03 commit 6e7166b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 3 deletions.
4 changes: 4 additions & 0 deletions extensions/systemd/systemd.js
Expand Up @@ -10,6 +10,10 @@ class SystemdProcessManager extends cli.ProcessManager {
return `ghost_${this.instance.name}`;
}

get logSuggestion() {
return 'journalctl -n 50';
}

start() {
this._precheck();

Expand Down
8 changes: 7 additions & 1 deletion lib/commands/restart.js
Expand Up @@ -4,14 +4,20 @@ const Command = require('../command');
class RestartCommand extends Command {
run() {
const instance = this.system.getInstance();
const restart = (() => {
return instance.process.restart(process.cwd(), this.system.environment)
.then(() => {
return instance.process.ensureStarted(process.cwd(), this.system.environment);
});
});

if (!instance.running()) {
return Promise.reject(new Error('Ghost instance is not currently running.'));
}

instance.loadRunningEnvironment(true);

return this.ui.run(instance.process.restart(process.cwd(), this.system.environment), 'Restarting Ghost');
return this.ui.run(restart, 'Restarting Ghost');
}
}

Expand Down
6 changes: 5 additions & 1 deletion lib/commands/run.js
Expand Up @@ -71,7 +71,11 @@ class RunCommand extends Command {
return;
}

instance.process.error(message.error);
// Ghost error log happens AFTER this event
// REMOVE WHEN ghost is released and we bump the cli engine?
setTimeout(() => {
instance.process.error(message.error);
}, 1000);
});
}

Expand Down
8 changes: 7 additions & 1 deletion lib/commands/start.js
Expand Up @@ -37,10 +37,16 @@ class StartCommand extends Command {

const start = () => {
return Promise.resolve(processInstance.start(process.cwd(), this.system.environment))
.then(() => { instance.running(this.system.environment); });
.then(() => {
return processInstance.ensureStarted(process.cwd(), this.system.environment);
})
.then(() => {
instance.running(this.system.environment);
});
};

return this.ui.run(start, 'Starting Ghost', runOptions).then(() => {
// @TODO: shouldn't this happen before start?
// If process manager doesn't support enable behavior OR it's already enabled, don't try to enable
if (!ProcessManager.supportsEnableBehavior(processInstance) || processInstance.isEnabled()) {
argv.enable = false;
Expand Down
68 changes: 68 additions & 0 deletions lib/process-manager.js
@@ -1,5 +1,7 @@
'use strict';
const net = require('net');
const every = require('lodash/every');
const errors = require('./errors');
const requiredMethods = [
'start',
'stop',
Expand All @@ -25,6 +27,10 @@ class ProcessManager {
this.instance = instance;
}

get logSuggestion() {
return 'ghost log';
}

/**
* Method called to start the Ghost process
*
Expand Down Expand Up @@ -60,6 +66,68 @@ class ProcessManager {
// Base Implementation
}

/**
* General implementation of figuring out if the Ghost blog has started successfully.
* Port long polling.
*
* @returns {Promise<any>}
*/
ensureStarted(cwd, env, options) {
options = options || {};

const intervalTimeoutInMS = options.intervalTimeoutInMS || 1000;
const maxTries = options.maxTries || 20;

let tries = 0;
let retryOnConnect = options.hasOwnProperty('retryOnConnect') ? options.retryOnConnect : true;

return new Promise((resolve, reject) => {
const interval = setInterval(() => {
const ghostSocket = net.connect(this.instance.config.get('server.port'));

ghostSocket.on('connect', (() => {
/**
* Try it one more time to connect to Ghost.
* By that, we ensure Ghost does not fall over after Ghost's express server has started.
*/
if (retryOnConnect) {
clearInterval(interval);
ghostSocket.destroy();

return this.ensureStarted(cwd, env, {
intervalTimeoutInMS: 2 * 1000,
maxTries: 3,
retryOnConnect: false
}).then(resolve).catch(reject);
}

clearInterval(interval);
ghostSocket.destroy();
resolve();
}));

ghostSocket.on('error', ((err) => {
if (tries > maxTries) {
clearInterval(interval);
ghostSocket.destroy();

// Force stop, otherwise extensions will restart Ghost and `ghost ls` will show it's running.
return Promise.resolve(this.stop(cwd, env))
.then(() => {
return reject(new errors.GhostError({
message: 'Ghost did not start.',
suggestion: this.logSuggestion,
err: err
}));
});
}

tries = tries + 1;
}));
}, intervalTimeoutInMS);
});
}

/**
* This function checks if this process manager can be used on this system
*
Expand Down
4 changes: 4 additions & 0 deletions lib/utils/local-process.js
Expand Up @@ -98,6 +98,10 @@ class LocalProcess extends ProcessManager {
});
}

ensureStarted() {
return Promise.resolve();
}

/**
* Called by the `ghost run` sub-process, this notifies the parent process that
* Ghost has started
Expand Down

0 comments on commit 6e7166b

Please sign in to comment.