Configuration validation in config-loader

Added a couple sanity checks to the config during the loadConfig call.

- Check that the config exists for the current NODE_ENV
- Check that the config.url exists and is valid structure
- Check that the config.database exists
- Check that the config.server exists and has a port and host value
This commit is contained in:
Jacob Gable 2013-09-23 22:37:36 -05:00 committed by Hannah Wolfe
parent 18ca744c98
commit 088518936c
3 changed files with 78 additions and 38 deletions

View File

@ -1,5 +1,7 @@
var fs = require('fs'),
when = require('when');
var fs = require('fs'),
url = require('url'),
when = require('when'),
errors = require('./server/errorHandling');
function writeConfigFile() {
var written = when.defer();
@ -11,19 +13,19 @@ function writeConfigFile() {
write;
if (!templateExists) {
throw new Error('Could not locate a configuration file. Please check your deployment for config.js or config.example.js.');
return errors.logError(new Error('Could not locate a configuration file.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.');
}
// Copy config.example.js => config.js
read = fs.createReadStream('config.example.js');
read.on('error', function (err) {
throw new Error('Could not open config.example.js for read.');
return errors.logError(new Error('Could not open config.example.js for read.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.');
});
read.on('end', written.resolve);
write = fs.createWriteStream('config.js');
write.on('error', function (err) {
throw new Error('Could not open config.js for write.');
return errors.logError(new Error('Could not open config.js for write.'), process.cwd(), 'Please check your deployment for config.js or config.example.js.');
});
read.pipe(write);
@ -32,15 +34,54 @@ function writeConfigFile() {
return written.promise;
}
function validateConfigEnvironment() {
var envVal = process.env.NODE_ENV || 'undefined',
config,
parsedUrl;
try {
config = require('../config')[envVal];
} catch (ignore) {
}
// Check if we don't even have a config
if (!config) {
errors.logError(new Error('Cannot find the configuration for the current NODE_ENV'), "NODE_ENV=" + envVal, 'Ensure your config.js has a section for the current NODE_ENV value');
return when.reject();
}
// Check that our url is valid
parsedUrl = url.parse(config.url || 'invalid');
if (!parsedUrl.protocol || !parsedUrl.host) {
errors.logError(new Error('Your site url in config.js is invalid.'), config.url, 'Please make sure this is a valid url before restarting');
return when.reject();
}
// Check that we have database values
if (!config.database) {
errors.logError(new Error('Your database configuration in config.js is invalid.'), JSON.stringify(config.database), 'Please make sure this is a valid Bookshelf database configuration');
return when.reject();
}
// Check for valid server host and port values
if (!config.server || !config.server.host || !config.server.port) {
errors.logError(new Error('Your server values (host and port) in config.js are invalid.'), JSON.stringify(config.server), 'Please provide them before restarting.');
return when.reject();
}
return when.resolve();
}
exports.loadConfig = function () {
var loaded = when.defer();
/* Check for config file and copy from config.example.js
if one doesn't exist. After that, start the server. */
fs.exists('config.js', function checkConfig(configExists) {
if (configExists) {
loaded.resolve();
validateConfigEnvironment().then(loaded.resolve).otherwise(loaded.reject);
} else {
writeConfigFile().then(loaded.resolve).otherwise(loaded.reject);
writeConfigFile().then(validateConfigEnvironment).then(loaded.resolve).otherwise(loaded.reject);
}
});
return loaded.promise;

View File

@ -130,14 +130,14 @@ Ghost.prototype.init = function () {
function doFirstRun() {
var firstRunMessage = [
"Welcome to Ghost.",
"You're running under the <strong>",
'Welcome to Ghost.',
'You\'re running under the <strong>',
process.env.NODE_ENV,
"</strong>environment.",
'</strong>environment.',
"Your URL is set to",
"<strong>" + self.config().url + "</strong>.",
"See <a href=\"http://docs.ghost.org/\">http://docs.ghost.org</a> for instructions."
'Your URL is set to',
'<strong>' + self.config().url + '</strong>.',
'See <a href="http://docs.ghost.org/">http://docs.ghost.org</a> for instructions.'
];
self.notifications.push({
@ -165,14 +165,13 @@ Ghost.prototype.init = function () {
}
// ### Initialisation
// make sure things are done in order
return when.join(
// Initialise the models
instance.dataProvider.init(),
self.dataProvider.init(),
// Calculate paths
instance.getPaths(),
self.getPaths(),
// Initialise mail after first run
instance.mail.init(self)
self.mail.init(self)
).then(function () {
// Populate any missing default settings
return models.Settings.populateDefaults();
@ -183,7 +182,7 @@ Ghost.prototype.init = function () {
return when.join(
// Check for or initialise a dbHash.
initDbHashAndFirstRun(),
// Initialize plugins
// Initialize plugins
self.initPlugins(),
// Initialize the permissions actions and objects
permissions.init()

View File

@ -12,6 +12,7 @@ var testUtils = require('./testUtils'),
describe("Ghost API", function () {
var testTemplatePath = 'core/test/unit/fixtures/',
themeTemplatePath = 'core/test/unit/fixtures/theme',
sandbox,
ghost;
before(function (done) {
@ -21,23 +22,26 @@ describe("Ghost API", function () {
});
beforeEach(function (done) {
sandbox = sinon.sandbox.create();
testUtils.initData().then(function () {
ghost = new Ghost();
done();
}, done);
});
it("is a singleton", function () {
var logStub = sinon.stub(console, "log"),
ghost1 = new Ghost(),
ghost2 = new Ghost();
afterEach(function () {
sandbox.restore();
});
should.strictEqual(ghost1, ghost2);
logStub.restore();
it("is a singleton", function () {
var ghost2 = new Ghost();
should.strictEqual(ghost, ghost2);
});
it("uses init() to initialize", function (done) {
var dataProviderInitMock = sinon.stub(ghost.dataProvider, "init", function () {
var dataProviderInitMock = sandbox.stub(ghost.dataProvider, "init", function () {
return when.resolve();
});
@ -49,8 +53,6 @@ describe("Ghost API", function () {
dataProviderInitMock.called.should.equal(true);
dataProviderInitMock.restore();
done();
}, done);
@ -59,7 +61,7 @@ describe("Ghost API", function () {
it("can register filters with specific priority", function () {
var filterName = 'test',
filterPriority = 9,
testFilterHandler = sinon.spy();
testFilterHandler = sandbox.spy();
ghost.registerFilter(filterName, filterPriority, testFilterHandler);
@ -72,7 +74,7 @@ describe("Ghost API", function () {
it("can register filters with default priority", function () {
var filterName = 'test',
defaultPriority = 5,
testFilterHandler = sinon.spy();
testFilterHandler = sandbox.spy();
ghost.registerFilter(filterName, testFilterHandler);
@ -84,9 +86,9 @@ describe("Ghost API", function () {
it("executes filters in priority order", function (done) {
var filterName = 'testpriority',
testFilterHandler1 = sinon.spy(),
testFilterHandler2 = sinon.spy(),
testFilterHandler3 = sinon.spy();
testFilterHandler1 = sandbox.spy(),
testFilterHandler2 = sandbox.spy(),
testFilterHandler3 = sandbox.spy();
ghost.registerFilter(filterName, 0, testFilterHandler1);
ghost.registerFilter(filterName, 2, testFilterHandler2);
@ -118,13 +120,13 @@ describe("Ghost API", function () {
});
it("loads templates for helpers", function (done) {
var compileSpy = sinon.spy(ghost, 'compileTemplate'),
var compileSpy = sandbox.spy(ghost, 'compileTemplate'),
pathsStub;
should.exist(ghost.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sinon.stub(ghost, "paths", function () {
pathsStub = sandbox.stub(ghost, "paths", function () {
return {
// Forcing the theme path to be the same
activeTheme: path.join(process.cwd(), testTemplatePath),
@ -145,20 +147,18 @@ describe("Ghost API", function () {
templateFn().should.equal('<h1>HelloWorld</h1>');
done();
}).then(null, done);
});
it("loads templates from themes first", function (done) {
var compileSpy = sinon.spy(ghost, 'compileTemplate'),
var compileSpy = sandbox.spy(ghost, 'compileTemplate'),
pathsStub;
should.exist(ghost.loadTemplate, 'load template function exists');
// In order for the test to work, need to replace the path to the template
pathsStub = sinon.stub(ghost, "paths", function () {
pathsStub = sandbox.stub(ghost, "paths", function () {
return {
activeTheme: path.join(process.cwd(), themeTemplatePath),
helperTemplates: path.join(process.cwd(), testTemplatePath)