Permalink
Browse files

Refactored so configuration not required, app conf by name, repo refe…

…rence
  • Loading branch information...
1 parent e7d9699 commit 1f60dac88861a159578a89e1b6818a0cec443548 @haraldrudell committed Jun 23, 2012
Showing with 121 additions and 65 deletions.
  1. +6 −2 app.js
  2. +3 −6 apps.json
  3. +23 −4 lib/appentity.js
  4. +2 −2 lib/godcontrol.js
  5. +43 −23 lib/godmodel.js
  6. +2 −1 lib/watchit.js
  7. +5 −1 package.json
  8. +37 −26 readme.md
View
8 app.js
@@ -1,7 +1,9 @@
// nodegod
// keeps a number of node apps running
var haraldops = require('haraldops')
-var defaults = haraldops.init({ appName: 'Node God', path: __dirname, logger: console.log })
+var defaults = haraldops.init({
+ appName: 'Node God', logger: console.log,
+ sessionSecret: 'veryGreat', PORT: 1111 })
// https://github.com/visionmedia/express
var express = require('express')
@@ -11,7 +13,7 @@ var godview = require('./routes/godview')
// Configuration
var app = module.exports = express.createServer()
godview.setTitle(defaults.init.appName)
-godcontrol.init(app, defaults, __dirname)
+godcontrol.init(app, defaults)
app.configure(function(){
app.set('views', __dirname + '/views')
app.set('view engine', 'ejs')
@@ -26,12 +28,14 @@ app.configure(function(){
app.use(app.router)
app.use(express.static(__dirname + '/public'))
})
+/*
require('ejsinbrowser').writeScript({
folder: app.settings.views,
ext: app.settings['view engine'],
jsGlobalVariable: 'NODEGOD',
templates: 'partials',
filename: __dirname + '/public/javascripts/templates.js'})
+*/
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }))
})
View
@@ -1,9 +1,6 @@
{
- "nodejs3": {
- "state": "run",
- "name": "LinkedIn",
- "watchFiles": [ "example.js" ],
- "start": "/Users/foxyboy/Desktop/airfox/node/linkedin/example/example.js",
- "folder": "/Users/foxyboy/Desktop/airfox/node/linkedin/example"
+ "My Cool Node One-Liner": {
+ "start": [ "--eval", "setTimeout(false, 10000)" ],
+ "launchBrowser": "http://localhost:1111"
}
}
View
@@ -34,7 +34,7 @@ function AppEntity(o) {
var self = this
var child // child process object
var commandQueue = [ ] // commands that affect state
- var watchFiles = o.watchFiles // watch file data from json
+ var watchFiles = o.watchFiles || [] // watch file data from json
var folder = o.folder // base folder for this app
var didKill // wether kill was issued to child process
var start = o.start
@@ -52,11 +52,11 @@ function AppEntity(o) {
var doRestart
if (o.name) this.name = o.name
- if (o.start && o.start != start) {
+ if (o.start && stringOrArrayDifferent(start, o.start)) {
start = o.start
doRestart = true
}
- if (o.watchFiles.length) {
+ if (o.watchFiles && o.watchFiles.length) {
watchFiles = o.watchFiles
doWatch = true
}
@@ -186,7 +186,7 @@ function AppEntity(o) {
}
function launchChild(debug) {
- var args = start.split(' ')
+ var args = Array.isArray(start) ? start : start.split(' ')
if (debug) args.unshift('--debug-brk')
didKill = false
child = child_process.spawn('node', args)
@@ -248,6 +248,25 @@ function AppEntity(o) {
}
+function stringOrArrayDifferent(o, o1) {
+ var different = false
+ if (o != o1) {
+ if (Array.isArray(o) && Array.isArray(o1) &&
+ o.length == o1.length) {
+ // two arrays of the same length: examine elements
+ // loop until true is returned
+ different = o.some(function(element, index) {
+ return element != o1[index]
+ })
+ } else different = true
+ }
+ return different
+}
+
+function isObject(o) {
+ return o != null && typeof o.valueOf() == 'object'
+}
+
// receive function for pushing model updates to clients
function eventListener(func) {
listener = func
View
@@ -45,8 +45,8 @@ function appEvent(app, isCrash) {
sendToClient(data)
}
-function init(app, defaults, dirname) {
+function init(app, defaults) {
sendToClient = godsocket.init(app, clientToControl)
- godmodel.loadAppFiles(defaults, dirname)
+ godmodel.loadAppFiles(defaults)
appentity.eventListener(appEvent)
}
View
@@ -19,52 +19,72 @@ module.exports = {
var appFiles = {}
var apps = {}
-var defaultFolder
+var parentFolder
+var launchedBrowser
-function loadAppFiles(defaults, appJsFolder) {
+// defaults come from haraldops
+function loadAppFiles(defaults) {
+ // initialize a file store used to store pids of running apps
+ // we use these if we happen to crash :)
var myStore = store.get(path.join(
defaults.init.tmpFolder,
defaults.init.identifier + 'store.json'))
appentity.setStore(myStore)
- // default is the parent folder of app.js or parent of cwd
- if (!defaultFolder) defaultFolder = path.resolve(appJsFolder, '..')
+ // default parentFolder is the parent folder of our application folder
+ parentFolder = path.join(defaults.init.appFolder, '..')
- // parse each appFile
- var fileArray = getfilenames.getFileArray(defaults.appFiles, undefined, 'json')
+ // find files containing app configurations
+ var fileArray = defaults.appFiles
+ var fileArrayFolder
+ if (fileArray) {
+ // we have a list of files: if we loaded a defaults file, default to that folder, otherwise node god's app folder
+ fileArrayFolder = defaults.init.defaultsFile ? path.dirname(defaults.init.defaultsFile) : defaults.init.appFolder
+ } else {
+ // no list of files: search for apps.json where possible settings file is and in nodegod's app folder
+ var f = 'apps'
+ fileArray = [ path.join(defaults.init.appFolder, f) ]
+ if (defaults.init.defaultsFile) fileArray.unshift(path.join(path.dirname(defaults.init.defaultsFile), f))
+ }
+ var fileArray = getfilenames.getFileArray(fileArray, fileArrayFolder, 'json')
fileArray.forEach(function (filename) {
processAppFile(filename)
appFiles[filename] = true
})
}
-// key is app identifier
+// key is app name
// name printable app name
// script
// state: initial state of the app
// watchFiles
function processAppFile(filename) {
console.log('process', filename)
- var fileApps = haraldops.loadJson(filename)
- for (var fileAppId in fileApps) {
-
- // prepare the fileApp object
- var fileApp = fileApps[fileAppId]
- fileApp.id = fileAppId
- if (!fileApp.folder) fileApp.folder = path.join(defaultFolder, fileAppId)
- if (!fileApp.name) fileApp.name = fileAppId
- if (!fileApp.start) {
- fileApp.start = path.join(defaultFolder, fileAppId, 'app.js')
+
+ var fileObject = haraldops.loadSettings(filename)
+ for (var appName in fileObject) {
+
+ // parse the fileApp object
+ var fileApp = fileObject[appName]
+ var o = {}
+ o.name = appName
+ o.id = fileApp.id || haraldops.createIdentifier(appName)
+ o.folder = fileApp.folder || path.join(parentFolder, o.id)
+ o.start = fileApp.start || path.join(o.folder, 'app.js')
+ o.state = fileApp.state || 'run'
+
+ if (fileApp.launchBrowser && !launchedBrowser) {
+ launchedBrowser = true
+ haraldops.browseTo(fileApp.launchBrowser)
}
// update or create the app entity
- var app = apps[fileAppId]
- if (app) {
- app.update(fileApp)
- } else {
- app = new appentity.AppEntity(fileApp)
- apps[fileAppId] = app
+ var app = apps[o.id]
+ if (app) app.update(o)
+ else {
+ app = new appentity.AppEntity(o)
+ apps[o.id] = app
app.doCommand()
}
}
View
@@ -20,7 +20,8 @@ function WatchIt(restartFunc) {
// return value: number of files being watched
this.updateFiles = function(fileValue, folder) {
- newFiles = getfilenames.getFileArray(fileValue, folder, 'js')
+ var newFiles = []
+ if (fileValue) newFiles = getfilenames.getFileArray(fileValue, folder, 'js')
if (!areStringArraysSame(files, newFiles)) {
files = newFiles
if (active) {
View
@@ -2,7 +2,7 @@
"name": "nodegod",
"description": "Web interface to node apps, file watch and crash restart by Harald Rudell",
"author": "Harald Rudell <harald@allgoodapps.com> (http://www.haraldrudell.com",
- "version": "0.0.2",
+ "version": "0.0.3",
"contributors": [
{
"name": "Harald Rudell",
@@ -18,6 +18,10 @@
"socket.io": "",
"haraldops": ""
},
+ "repository" : {
+ "type" : "git",
+ "url" : "https://haraldrudell@github.com/haraldrudell/nodegod.git"
+ },
"scripts": {
"start": "node app"
}
View
@@ -1,55 +1,66 @@
# Node God
Launches node applications, restarts them on crash or file updates, produces a shared log.
-# Required Configuration
-Configured by JSON-files in the filesystem
+# Run as demo
-## Node God itself
-* nodegod.js needs to be in the folder
- * $HOME
- * $HOME/apps
- * the folder were Node God's app.js resides
+```js
+nodegod$ node app
+
+
+=== 2012-06-23 13:24:21 Node God starting
+ info - socket.io started
+process /home/foxyboy/Desktop/c505/node/nodegod/apps.json
+Application Node God on node v0.6.14 available on port 1111 in development mode
+127.0.0.1
+```
+
+opens a browser window that monitors an app that exits every 10 seconds.
+
+# Configuration Files
+
+## nodegod.json
+Node God itself is configured using a json file that provides port number, a session secret and the location of other json files containing application configurations. The paths searched for nodegod.json are:
+* $HOME/apps
+* $HOME
+* Node God's launch folder, where app.js is located
```js
{
- "PORT": 1111,
- "haraldops": {
- },
"appFiles": "/home/foxyboy/apps/apps.json",
- "sessionSecret": "verygreat"
}
```
-* PORT: optional number: the port for Node God, ovverriden by env.PORT, default 3000
-* appFiles: required path: where to find json file with app configurations
-* sessionSecret: required string, secret for web session
+* PORT: optional number: the port for Node God, defaults to env.PORT or 1111
+* appFiles: optional string or array of strings: filenames to search for app configurations.
+ * if strings are not fully qualified paths, the folder where nodegod.json was found or node god's app folder are searched
+ * default: apps.json in either the noegod.json folder or nodegod's app folder
+* sessionSecret: optional string, has a default value
* defaultFolder: a parent folder for deployed apps, default the parent folder of where nodegod's app.js is located
-Note: pids are stored at $HOME/tmp or the global temp folder
+Note: pids are stored in a file at $HOME/tmp or the global temp folder
-## Configuring apps
+## Configuring files for monitored apps
```js
{
- "nodejs3": {
- "state": "run",
- "name": "Node.js #3",
+ "Node.js #3": {
"watchFiles": [ "package.json", "app.js", "lib", "routes", "/home/foxyboy/apps/nodejs3.json" ]
}
}
```
-* Key: the identifier (computer-friendly string) used for this app, here "nodejs3." This is the name of the default deployment folder for this app
-* state: the initial state of the app: run/stop/debug
-* folder: optional: the folder where the app is deployed, default as described under nodegod settings
-* start: path to the JavaScript launching the app, default app.js
-* name: Human readable app name, defaule is Key
-* watchFiles: a single entry or array of filenames and folders to watch. If any file changes the app is restarted
+* Key: the name of this app
+* id: optional string: the identifier (computer-friendly string) used for this app. default is derived from the app name, for "Node.js #3" here it would be "nodejs3"
+* state: optional string: the initial state of the app: run/stop/debug, default run
+* folder: optional path: the folder where the app is deployed. Default is a sibling folder to nodegod, ie. the parent folder of nodegod with id appended.
+* start: optional string or array of strings: parameters to the node executable, default app.js in the app's folder
+* watchFiles: optional string or array of Strings: filenames and folders to watch. If any file changes the app is restarted
+* launchBrowser: optional string: url for which a browser window is launched once
# Features
* Web frontend for managing any number of apps
* App lifecycle management with states and transitions for run/stop/debug
* App is automatically restarted unless it crashes in less than 3 seconds
-* If app state is crashed, watchers are still active so the app relaunches on update
+* If app state is crashed, watchers are still active so relaunch os attempted on file updates
* File watchers restart the app after a 3 second delay, so that all file writes have time to complete
* Ability to reload app configurations as files are added
* If Node God crashes, it will relaunch managed apps on restart so that they become managed

0 comments on commit 1f60dac

Please sign in to comment.