This repository has been archived by the owner on Jul 17, 2018. It is now read-only.
forked from tbaum/neo4j-shutdown-proxy
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ceedb94
Showing
5 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
|
||
# $ requires npm install connect into | ||
# require.paths [ '$HOME/.node_modules', '$HOME/.node_libraries', '/usr/lib/node' ] | ||
|
||
fs = require 'fs' | ||
connect = require 'connect' | ||
|
||
process.on 'uncaughtException', (err) -> | ||
console.log "Type: " + err.type | ||
console.log "Message: " + err.message | ||
console.log "Arguments: " + err.arguments | ||
console.log err.stack | ||
|
||
neo4jconfig = require "./neo4jconfig.coffee" | ||
neo4jserver = require "./neo4jserver.coffee" | ||
proxy = require "./proxy.coffee" | ||
|
||
class ServerManager | ||
constructor: -> | ||
proxies = {} | ||
auth = { user: "admin", pass: "admin"} | ||
|
||
bringUp = (id) -> | ||
config = neo4jconfig.parse id | ||
neo4j = neo4jserver.create config | ||
proxies[id] = proxy.buildFor neo4j, config | ||
|
||
storeConfig = -> | ||
config = | ||
auth : auth | ||
proxies: (key for key of proxies) | ||
fs.writeFileSync "config.json", JSON.stringify(config) | ||
|
||
@loadConfig = -> | ||
try | ||
config = JSON.parse(fs.readFileSync "config.json") | ||
auth = config['auth'] | ||
for i in config['proxies'] | ||
try bringUp i catch e | ||
console.log "error during startup for "+ i + " " + e | ||
catch error | ||
console.log error | ||
|
||
@start = -> | ||
connect( | ||
connect.basicAuth (user, pass) -> auth.user == user && auth.pass == pass | ||
connect.router (app) -> | ||
app.get '/', (request, response) -> response.end JSON.stringify(key for key of proxies) | ||
|
||
app.post '/:id', (request, response) -> | ||
id = request.params.id | ||
if (proxies[id]) then throw new Error "instance " + id + " is already registered" | ||
bringUp id | ||
storeConfig() | ||
response.end "add "+request.params.id | ||
|
||
app.delete '/:id', (request, response) -> | ||
id = request.params.id | ||
if (!proxies[id]) then throw new Error "instance " + id + " is not registered" | ||
proxies[id].stop() | ||
delete proxies[id] | ||
storeConfig() | ||
response.end "delete "+request.params.id | ||
|
||
).listen(7999) | ||
|
||
serverManager = new ServerManager() | ||
serverManager.loadConfig() | ||
serverManager.start() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
exec = require("child_process").exec | ||
|
||
class IptablesRule | ||
constructor: (rule) -> | ||
isPresent = (condition, callback) -> | ||
exec "iptables -tnat -S PREROUTING", (x, o) -> | ||
if ( (o.indexOf(rule) != -1) == condition) then callback() | ||
|
||
@addRule = -> | ||
isPresent false, -> | ||
exec "iptables -tnat -A PREROUTING " + rule | ||
|
||
@removeRule = -> | ||
isPresent true, -> | ||
exec "iptables -tnat -D PREROUTING " + rule | ||
|
||
|
||
exports.rule = (rule) -> new IptablesRule rule | ||
|
||
exports.redirectRule = (port, proxyPort) -> | ||
new IptablesRule "-i eth0 -p tcp -m tcp --dport " + port + " -j REDIRECT --to-ports " + proxyPort |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
fs = require 'fs' | ||
|
||
class Neo4JConfiguration | ||
constructor: (@instance) -> | ||
home = "/mnt/" + @instance | ||
configData = fs.readFileSync home + "/conf/neo4j-server.properties", "UTF-8" | ||
console.log "parse config for " + @instance | ||
|
||
@port = Number /org.neo4j.server.webserver.port=(.+)/.exec(configData)[1] | ||
@adminCredentials = /org.neo4j.server.credentials=(.+)/.exec(configData)[1] | ||
@proxyPort = this.port + 1000 | ||
@startCmd = home + "/bin/neo4j start" | ||
@statusCmd = home + "/bin/neo4j status" | ||
@stopCmd = home + "/bin/neo4j stop" | ||
|
||
exports.parse = (instance) -> new Neo4JConfiguration instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
http = require "http" | ||
iptables = require "./iptables.coffee" | ||
|
||
exec_org = require('child_process').exec | ||
|
||
exec = (log, cmd, callback) -> | ||
log "exec:"+cmd | ||
exec_org cmd, (exit, out, err)-> | ||
if (exit) then log "exit-code: "+exit | ||
if (out) then log " > " + line for line in out.split "\n" | ||
if (err) then log "E> " + line for line in err.split "\n" | ||
if (callback) then callback exit, out, err | ||
|
||
|
||
class Neo4JServer | ||
constructor: (@config) -> | ||
iptRule = iptables.redirectRule config.port, config.proxyPort | ||
running = false | ||
starting = false | ||
config = @config | ||
|
||
log = (message) -> console.log config.instance + " " + message | ||
|
||
setRunning = (r) -> | ||
running = r | ||
if (running) | ||
log "remove firewall rule" | ||
iptRule.removeRule() | ||
else | ||
log "add firewall rule" | ||
iptRule.addRule() | ||
|
||
updateStatus = @updateStatus = -> | ||
exec log, config.statusCmd, (error, stdout) -> | ||
setRunning(stdout.indexOf("not running") == -1) | ||
checkIdle() | ||
|
||
checkIdle = @checkIdle = -> | ||
if (running) | ||
request = http.request | ||
port: config.port, method: 'GET', path: '/admin/statistic/', host: '127.0.0.1', | ||
headers: | ||
Host:'localhost', Accept:"application/json" | ||
Authorization: 'Basic ' + new Buffer(config.adminCredentials).toString('base64'), | ||
(response)-> | ||
now = new Date().getTime() / 1000 | ||
response.setEncoding 'utf8' | ||
data = "" | ||
response.on 'data', (chunk) -> data += chunk | ||
response.on 'end', -> | ||
requestCount = 0 | ||
period = 0 | ||
for i in JSON.parse(data) | ||
if (i['timeStamp'] > (now - 7200)) | ||
requestCount += i['requests'] | ||
period += i['period'] | ||
|
||
log "request-count for "+ config.port + " == " + requestCount + "/" + period | ||
|
||
request.on 'error', (e) -> | ||
log "problem with request: " + e.message | ||
setRunning false | ||
|
||
request.end "\n" | ||
|
||
startServer = @startServer = -> | ||
log "try to start server" | ||
if (starting || running) | ||
log "is running(=" + running + ") or starting(=" + starting + ")" | ||
return | ||
|
||
starting = true | ||
log "starting" | ||
exec log, config.startCmd, (error, stdout, stderr) -> | ||
starting = false | ||
if (error == null) then setRunning true | ||
else if (stdout.indexOf("already running with pid") != -1) then updateStatus() | ||
|
||
@stopServer = -> | ||
log "try to stop server" | ||
if (!running && !starting) | ||
log "not running(=" + running + ") or starting(=" + starting + ")" | ||
return | ||
|
||
starting = true | ||
log "stopping" | ||
exec log, config.stopCmd, (error, stdout, stderr) -> | ||
starting = false | ||
setRunning false | ||
|
||
waitForServer = @waitForServer = (msg, callback) -> | ||
if (running) | ||
callback() | ||
else | ||
log "wait " + msg | ||
if (!starting) then startServer() | ||
setTimeout waitForServer, 2000, msg, callback | ||
|
||
|
||
exports.create = (config) -> new Neo4JServer(config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
|
||
net = require 'net' | ||
|
||
class Proxy | ||
constructor: (neo4j, config) -> | ||
log = (message) -> console.log config.instance + " " + message | ||
|
||
server = net.createServer (proxySocket) -> | ||
info = proxySocket.remoteAddress + ":" + proxySocket.remotePort | ||
log "handle connection " + info | ||
|
||
serverSocket = new net.Socket() | ||
proxySocket.on "data", (data) -> neo4j.waitForServer ">" + info, -> serverSocket.write data | ||
proxySocket.on "close", -> neo4j.waitForServer ">" + info, -> serverSocket.end() | ||
|
||
neo4j.waitForServer "<" + info, -> | ||
serverSocket.connect config.port, "127.0.0.1" | ||
serverSocket.on "data", (data) -> proxySocket.write data | ||
serverSocket.on "close", (had_error) -> proxySocket.end() | ||
|
||
log "start " + config.proxyPort + " -> " + config.port | ||
list = server.listen config.proxyPort | ||
|
||
updater = setInterval neo4j.checkIdle, 30000 | ||
neo4j.updateStatus() | ||
|
||
@stop = -> | ||
clearInterval updater | ||
neo4j.startServer() | ||
server.close() | ||
|
||
exports.buildFor = (neo4j, config) -> new Proxy neo4j, config |