Moved notify out of GhostServer

- make this a standalone module
This commit is contained in:
Hannah Wolfe 2021-02-16 15:29:40 +00:00
parent b65cb7bd7b
commit 2527efd6fc
5 changed files with 90 additions and 74 deletions

View File

@ -187,6 +187,19 @@ function mountGhost(rootApp, ghostApp) {
debug('End: mountGhost');
}
// @TODO: make this notification different
function notifyReadiness(error) {
const notify = require('./server/notify');
if (error) {
debug('Notifying readiness (error)');
notify.notifyServerStarted(error);
} else {
debug('Notifying readiness (success)');
notify.notifyServerStarted();
}
}
/**
* ----------------------------------
* Boot Ghost - The magic starts here
@ -255,9 +268,8 @@ async function bootGhost() {
// We are technically done here
bootLogger.log('booted');
// @TODO: make this notification different
// debug('boot notifying readiness');
// GhostServer.notifyServerStarted();
notifyReadiness();
// Init our background jobs, we don't wait for this to finish
initRecurringJobs({config});
@ -267,8 +279,7 @@ async function bootGhost() {
return ghostServer;
} catch (error) {
const errors = require('@tryghost/errors');
// @TODO: fix these extra requires
const GhostServer = require('./server/ghost-server');
// @TODO: fix this extra require
const logging = require('./shared/logging');
let serverStartError = error;
@ -278,7 +289,7 @@ async function bootGhost() {
}
logging.error(serverStartError);
GhostServer.notifyServerStarted(serverStartError);
notifyReadiness(serverStartError);
// If ghost was started and something else went wrong, we shut it down
if (ghostServer) {

View File

@ -11,8 +11,8 @@ const urlUtils = require('./../shared/url-utils');
const errors = require('@tryghost/errors');
const {i18n} = require('./lib/common');
const logging = require('../shared/logging');
const notify = require('./notify');
const moment = require('moment');
const bootstrapSocket = require('@tryghost/bootstrap-socket');
const stoppable = require('stoppable');
/**
@ -109,7 +109,7 @@ class GhostServer {
}
debug('server notifying started');
return GhostServer.notifyServerStarted()
return notify.notifyServerStarted()
.finally(() => {
resolve(self);
});
@ -288,55 +288,3 @@ class GhostServer {
}
module.exports = GhostServer;
/**
* We call notify server started when the server is ready to serve traffic
* When the server is started, but not ready, it is only able to serve 503s
*
* If the server isn't able to reach started, notifyServerStarted is called with an error
* A status message, any error, and debug info are all passed to managing processes via IPC and the bootstrap socket
*/
let notifyServerStartedCalled = false;
const debugInfo = {
versions: process.versions,
platform: process.platform,
arch: process.arch,
release: process.release
};
module.exports.notifyServerStarted = function (error = null) {
// If we already sent a ready notification, we should not do it again
if (notifyServerStartedCalled) {
return Promise.resolve();
}
// Mark this function as called
notifyServerStartedCalled = true;
// Build our message
// - if there's no error then the server is ready
let message = {
started: true,
debug: debugInfo
};
// - if there's an error then the server is not ready, include the errors
if (error) {
message.started = false;
message.error = error;
}
// CASE: IPC communication to the CLI for local process manager
if (process.send) {
process.send(message);
}
// CASE: use bootstrap socket to communicate with CLI for systemd
let socketAddress = config.get('bootstrap-socket');
if (socketAddress) {
return bootstrapSocket.connectAndSend(socketAddress, logging, message);
}
return Promise.resolve();
};

57
core/server/notify.js Normal file
View File

@ -0,0 +1,57 @@
/**
* We call notify server started when the server is ready to serve traffic
* When the server is started, but not ready, it is only able to serve 503s
*
* If the server isn't able to reach started, notifyServerStarted is called with an error
* A status message, any error, and debug info are all passed to managing processes via IPC and the bootstrap socket
*/
// Required Ghost internals
const config = require('../shared/config');
const logging = require('../shared/logging');
let notifyServerStartedCalled = false;
const debugInfo = {
versions: process.versions,
platform: process.platform,
arch: process.arch,
release: process.release
};
module.exports.notifyServerStarted = function (error = null) {
// If we already sent a ready notification, we should not do it again
if (notifyServerStartedCalled) {
return Promise.resolve();
}
// Mark this function as called
notifyServerStartedCalled = true;
// Build our message
// - if there's no error then the server is ready
let message = {
started: true,
debug: debugInfo
};
// - if there's an error then the server is not ready, include the errors
if (error) {
message.started = false;
message.error = error;
}
// CASE: IPC communication to the CLI for local process manager
if (process.send) {
process.send(message);
}
// CASE: use bootstrap socket to communicate with CLI for systemd
let socketAddress = config.get('bootstrap-socket');
if (socketAddress) {
const bootstrapSocket = require('@tryghost/bootstrap-socket');
return bootstrapSocket.connectAndSend(socketAddress, logging, message);
}
return Promise.resolve();
};

View File

@ -6,16 +6,16 @@ const {events} = require('../../../core/server/lib/common');
const bootstrapSocket = require('@tryghost/bootstrap-socket');
describe('GhostServer', function () {
describe('Notify', function () {
describe('notifyServerStarted', function () {
let GhostServer;
let notify;
let socketStub;
let eventSpy;
beforeEach(function () {
// Have to re-require each time to clear the internal flag
delete require.cache[require.resolve('../../../core/server/ghost-server')];
GhostServer = require('../../../core/server/ghost-server');
delete require.cache[require.resolve('../../../core/server/notify')];
notify = require('../../../core/server/notify');
// process.send isn't set for tests, we can safely override;
process.send = sinon.stub();
@ -35,11 +35,11 @@ describe('GhostServer', function () {
});
it('it resolves a promise', function () {
GhostServer.notifyServerStarted().should.be.fulfilled();
notify.notifyServerStarted().should.be.fulfilled();
});
it('it communicates with IPC correctly on success', function () {
GhostServer.notifyServerStarted();
notify.notifyServerStarted();
process.send.calledOnce.should.be.true();
@ -50,7 +50,7 @@ describe('GhostServer', function () {
});
it('communicates with IPC correctly on failure', function () {
GhostServer.notifyServerStarted(new Error('something went wrong'));
notify.notifyServerStarted(new Error('something went wrong'));
process.send.calledOnce.should.be.true();
@ -64,7 +64,7 @@ describe('GhostServer', function () {
it('communicates via bootstrap socket correctly on success', function () {
configUtils.set('bootstrap-socket', 'testing');
GhostServer.notifyServerStarted();
notify.notifyServerStarted();
socketStub.calledOnce.should.be.true();
socketStub.firstCall.args[0].should.eql('testing');
@ -79,7 +79,7 @@ describe('GhostServer', function () {
it('communicates via bootstrap socket correctly on failure', function () {
configUtils.set('bootstrap-socket', 'testing');
GhostServer.notifyServerStarted(new Error('something went wrong'));
notify.notifyServerStarted(new Error('something went wrong'));
socketStub.calledOnce.should.be.true();
socketStub.firstCall.args[0].should.eql('testing');
@ -95,9 +95,9 @@ describe('GhostServer', function () {
it('can be called multiple times, but only communicates once', function () {
configUtils.set('bootstrap-socket', 'testing');
GhostServer.notifyServerStarted();
GhostServer.notifyServerStarted(new Error('something went wrong'));
GhostServer.notifyServerStarted();
notify.notifyServerStarted();
notify.notifyServerStarted(new Error('something went wrong'));
notify.notifyServerStarted();
process.send.calledOnce.should.be.true();
socketStub.calledOnce.should.be.true();

View File

@ -14,10 +14,10 @@ const knexMigrator = new KnexMigrator();
// Ghost Internals
const config = require('../../core/shared/config');
const boot = require('../../core/boot');
const GhostServer = require('../../core/server/ghost-server');
const {events} = require('../../core/server/lib/common');
const db = require('../../core/server/data/db');
const models = require('../../core/server/models');
const notify = require('../../core/server/notify');
const urlService = require('../../core/frontend/services/url');
const settingsService = require('../../core/server/services/settings');
const frontendSettingsService = require('../../core/frontend/services/settings');
@ -252,7 +252,7 @@ const freshModeGhostStart = async (options) => {
await bootGhost(options);
// Ensure notify was called (this is idempotent)
GhostServer.notifyServerStarted();
notify.notifyServerStarted();
// Wait for the URL service to be ready, which happens after boot, but don't re-trigger db.ready
await urlServiceUtils.isFinished({disableDbReadyEvent: true});