forked from koding/koding
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.coffee
More file actions
175 lines (154 loc) · 5.93 KB
/
Copy pathmain.coffee
File metadata and controls
175 lines (154 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
amqp = require 'amqp'
hat = require 'hat'
os = require 'os'
{EventEmitter} = require 'events'
fs = require "fs"
nodePath = require 'path'
{environment} = require 'koding-config-manager'
class Worker extends EventEmitter
@throwIt = (it) ->
console.error it if it?
constructor:(options = {},data)->
options.name or= "worker"
options.serviceGenericName or= ""
options.serviceUniqueName or= ""
options.status or= 1
options.command or= ""
options.option or= "many"
options.uuid or= hat()
options.hostname or= os.hostname()
options.pid or= process.pid
options.proxyName or= ""
options.interval or= 10
options.port or= 0
options.rabbitKey or= ""
options.binary or= no
options.mq or=
host : 'localhost'
port : '5672'
login : 'guest'
password : 'guest'
vhost : '/'
super options, data
@defaultInit options, data
defaultInit:(options, data)->
@reporter = {}
@interval = options.interval
@amqpOptions = options.mq
@binary = options.binary
@worker =
name : options.name
proxyName : options.proxyName
serviceGenericName : options.serviceGenericName
serviceUniqueName : options.serviceUniqueName
message :
command : options.command
option : options.option
uuid : options.uuid
hostname : options.hostname
timestamp : @isoDateString()
pid : options.pid
status : options.status
version : parseInt(@getVersion())
environment : environment
port : options.port
monitor :
mem :
rss : 0
heapTotal : 0
heapUsed : 0
unit : 'MB'
uptime : 0
publish: (command) ->
@fetchWorkerExchange (workerExchange) =>
@worker.message.command = command
@worker.timestamp = @isoDateString()
@worker.monitor.uptime += @interval
workerExchange.publish "input.worker", { @worker },
appId: "#{@worker.uuid}"
startReporter: (command) ->
@reporter[command] = setInterval =>
@publish command
, @interval * 1000
stopReporter: (command) ->
clearInterval @reporter[command]
isoDateString: ->
d = new Date()
pad = (n) ->
(if n < 10 then "0" + n else n)
d.getUTCFullYear() + "-" + pad(d.getUTCMonth() + 1) + "-" + pad(d.getUTCDate()) + "T" + pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes()) + ":" + pad(d.getUTCSeconds()) + "Z"
getVersion:() ->
versionFile = nodePath.join(__dirname, '../../VERSION')
if fs.existsSync versionFile
version = (fs.readFileSync versionFile, 'utf-8').trim()
return version ? "0.0.1"
getRabbitKey:() ->
version = (fs.readFileSync nodePath.join(__dirname, '../../KEY'), 'utf-8').trim()
handleMessage:(message, headers, deliveryInfo)=>
messageData = JSON.parse message.data.toString("utf-8")
unless messageData.name?
return
unless messageData.uuid is @worker.uuid
return
switch messageData.command
when 'start', 'add', 'first.start' #backward compability, last two keywords will be removed
@emit 'permissionToRun'
when 'noPermission', 'added.before' #same as above, backward compability
console.log "[#{@worker.name} - #{@isoDateString()}] no permission. #{messageData.log}"
when 'kill'
@emit 'killWorkerProcess'
when 'killForce'
@emit 'killForceWorkerProcess'
else
console.log "received command unknown: #{messageData.command}"
fetchExchange:(name, options, callback)->
exchangeName = "#{name}Exchange"
readyEvent = "#{exchangeName}Ready"
if @[exchangeName] is null
@once readyEvent, => @fetchWorkerExchange callback
else unless @workerExchange?
@[exchangeName] = null
@connection.exchange exchangeName, options,
(exchange)=>
@[exchangeName] = exchange
@emit readyEvent
callback exchange
else callback @[exchangeName]
fetchWorkerExchange:(callback)->
@fetchExchange 'worker', @getWorkerExchangeOptions(), callback
getWorkerExchangeOptions:->
type : 'topic'
autoDelete : no
durable : true
connect: ()->
{host, port, protocol, login, password, vhost, heartbeat, autoReconnect} = @amqpOptions
port ?= 5672
protocol ?= 'amqp:'
options = {host, port, protocol} # but not vhost, because of a bug in node-amqp
options.login = login if login?
options.password = password if password?
options.vhost = vhost if vhost?
options.heartbeat = heartbeat if heartbeat?
options.autoReconnect = autoReconnect ? no
@connection = amqp.createConnection options
@connection.on "error", Worker.throwIt
@connection.on "ready", =>
@connection.exchange "workerExchange", @getWorkerExchangeOptions(), (exchange) =>
@connection.queue "#{@worker.name}-#{@worker.hostname}-#{@worker.uuid}",
durable: false
autoDelete: true
, (queue) =>
queue.bind 'workerExchange', "output.worker.#{@worker.uuid}"
queue.on "queueBindOk", =>
unless @binary
if @worker.message.option is "one" or @worker.message.option is "version"
@startReporter "add"
else
@fetchWorkerExchange (workerExchange)=>
workerExchange.publish "input.worker", {worker: @worker},
appId: "#{@worker.uuid}"
else
@emit 'permissionToRun'
queue.subscribe (message, headers, deliveryInfo) =>
@handleMessage message, headers, deliveryInfo
module.exports = Worker