1 | var j, len, logstream, openLogFile, ref, root;
|
2 |
|
3 | const cluster = require('cluster');
|
4 | const domain = require('domain');
|
5 | const fs = require('fs');
|
6 | const path = require('path');
|
7 | const yaml = require('js-yaml');
|
8 |
|
9 | let project_root = process.env.PROJECT_ROOT;
|
10 | if (/versions\/\d{4}-\d{2}-\d{2},\d{2}-\d{2},[a-z0-9]{7}$/.test(project_root)) {
|
11 | project_root = path.resolve(project_root, '..', '..', 'current');
|
12 | }
|
13 | project_root = path.relative(process.cwd(), project_root);
|
14 | if (!project_root) {
|
15 | project_root = '.';
|
16 | }
|
17 |
|
18 | const app_dir = project_root + '/app';
|
19 | const config_dir = project_root + '/config';
|
20 |
|
21 | let shutdowning = false;
|
22 |
|
23 | const config = yaml.safeLoad(fs.readFileSync(project_root + '/deploy.yaml', 'utf-8'));
|
24 | let do_watch = false;
|
25 | let devel_mode = false;
|
26 | let redirect_log = false;
|
27 |
|
28 | ref = process.argv;
|
29 | for (j = 0, len = ref.length; j < len; j++) {
|
30 | const arg = ref[j];
|
31 | if (arg === '-w') {
|
32 | do_watch = true;
|
33 | }
|
34 | if (arg === '-d') {
|
35 | devel_mode = true;
|
36 | }
|
37 | if (arg === '-l') {
|
38 | redirect_log = true;
|
39 | }
|
40 | }
|
41 |
|
42 | if (redirect_log) {
|
43 | logstream = void 0;
|
44 | openLogFile = function() {
|
45 | if (logstream) {
|
46 | logstream.close();
|
47 | }
|
48 | logstream = fs.createWriteStream(root + "/" + config.project + ".log", {
|
49 | flags: 'a+',
|
50 | mode: '0644',
|
51 | encoding: 'utf8'
|
52 | });
|
53 | };
|
54 | root = path.join(process.env.HOME, '.croquis');
|
55 | try {
|
56 | fs.mkdirSync(root, '0755');
|
57 | } catch (error) {}
|
58 | openLogFile();
|
59 | process.stdout.write = function(chunk, encoding, cb) {
|
60 | logstream.write(chunk, encoding, cb);
|
61 | };
|
62 | process.stderr.write = function(chunk, encoding, cb) {
|
63 | logstream.write(chunk, encoding, cb);
|
64 | };
|
65 | process.on('SIGUSR2', function() {
|
66 | openLogFile();
|
67 | });
|
68 | }
|
69 |
|
70 | function log(msg) {
|
71 | console.log("[" + (Date.now()) + "] [deployer] " + msg);
|
72 | }
|
73 |
|
74 | function debug(msg) {
|
75 |
|
76 | }
|
77 |
|
78 | function exitIfNoWorkers() {
|
79 | if (Object.keys(cluster.workers).length === 0) {
|
80 | console.log("[" + (Date.now()) + "] [deployer] Terminate");
|
81 | process.exit(0);
|
82 | }
|
83 | }
|
84 |
|
85 | function registerHandlers() {
|
86 | cluster.on('exit', function(worker, code, signal) {
|
87 | var options;
|
88 | if (shutdowning) {
|
89 | exitIfNoWorkers();
|
90 | return;
|
91 | }
|
92 | if (!worker.prevent_restart) {
|
93 | options = worker.options;
|
94 | if (options["try"] < 3) {
|
95 | fork(options);
|
96 | }
|
97 | }
|
98 | });
|
99 | }
|
100 |
|
101 | function fork(options) {
|
102 | var worker;
|
103 | log('forking... ' + options.exec);
|
104 | options["try"]++;
|
105 | cluster.setupMaster();
|
106 | cluster.settings.exec = __dirname + '/run_app.js';
|
107 | cluster.settings.args = [project_root, options.exec];
|
108 | if (redirect_log) {
|
109 | cluster.settings.silent = true;
|
110 | }
|
111 | worker = cluster.fork({
|
112 | WORKER_NUM: options.num
|
113 | });
|
114 | worker.options = options;
|
115 | if (redirect_log) {
|
116 | worker.process.stdout.on('data', function(data) {
|
117 | logstream.write(data);
|
118 | });
|
119 | worker.process.stderr.on('data', function(data) {
|
120 | logstream.write(data);
|
121 | });
|
122 | }
|
123 | worker.once('listening', function() {
|
124 | worker.options["try"] = 0;
|
125 | });
|
126 | return worker;
|
127 | }
|
128 |
|
129 | function startWorkers() {
|
130 | var count, i, k, l, len1, numCPUs, ref1, ref2, worker;
|
131 | numCPUs = require('os').cpus().length;
|
132 | if (devel_mode) {
|
133 | numCPUs = 1;
|
134 | }
|
135 | ref1 = config.workers;
|
136 | for (k = 0, len1 = ref1.length; k < len1; k++) {
|
137 | worker = ref1[k];
|
138 | if (devel_mode && worker.production_only === true) {
|
139 | continue;
|
140 | }
|
141 | if (worker.instances === 'max') {
|
142 | count = numCPUs;
|
143 | } else {
|
144 | count = parseInt(worker.instances);
|
145 | }
|
146 | if (!(count > 0)) {
|
147 | count = 1;
|
148 | }
|
149 | for (i = l = 0, ref2 = count; 0 <= ref2 ? l < ref2 : l > ref2; i = 0 <= ref2 ? ++l : --l) {
|
150 | fork({
|
151 | exec: app_dir + '/' + worker.app,
|
152 | num: i,
|
153 | graceful_exit: worker.graceful_exit,
|
154 | "try": 0
|
155 | });
|
156 | }
|
157 | }
|
158 | }
|
159 |
|
160 | function restartWorkers() {
|
161 | var id, killOne, ref1, worker, workers;
|
162 | workers = [];
|
163 | ref1 = cluster.workers;
|
164 | for (id in ref1) {
|
165 | worker = ref1[id];
|
166 | workers.push(worker);
|
167 | }
|
168 | if (workers.length === 0) {
|
169 |
|
170 | startWorkers();
|
171 | return;
|
172 | }
|
173 | killOne = function() {
|
174 | if (workers.length > 0) {
|
175 | worker = workers.pop();
|
176 | worker.once('disconnect', function() {
|
177 | killOne();
|
178 | });
|
179 | worker.options["try"] = 0;
|
180 | if (worker.options.graceful_exit) {
|
181 | worker.prevent_restart = true;
|
182 | fork(worker.options).once('listening', function() {
|
183 | debug('killing... ' + worker.options.exec);
|
184 | process.kill(worker.process.pid, 'SIGTERM');
|
185 | }).once('exit', function() {
|
186 |
|
187 | workers.length = 0;
|
188 | });
|
189 | } else {
|
190 | debug('killing... ' + worker.options.exec);
|
191 | process.kill(worker.process.pid, 'SIGTERM');
|
192 | }
|
193 | }
|
194 | };
|
195 | killOne();
|
196 | }
|
197 |
|
198 | function startWatch() {
|
199 | var extensions, ignoreDirectories, traverse, watch;
|
200 | ignoreDirectories = [];
|
201 | extensions = ['.js', '.coffee', '.ts'];
|
202 | watch = function(file) {
|
203 | if (extensions.indexOf(path.extname(file)) < 0) {
|
204 | return;
|
205 | }
|
206 |
|
207 | fs.watchFile(file, {
|
208 | interval: 100
|
209 | }, function(curr, prev) {
|
210 | if (curr.mtime > prev.mtime) {
|
211 | log('changed - ' + file);
|
212 | restartWorkers();
|
213 | }
|
214 | });
|
215 | };
|
216 | traverse = function(file) {
|
217 | fs.stat(file, function(err, stat) {
|
218 | if (err) {
|
219 | return;
|
220 | }
|
221 | if (stat.isDirectory()) {
|
222 | if (ignoreDirectories.indexOf(path.basename(file)) >= 0) {
|
223 | return;
|
224 | }
|
225 | fs.readdir(file, function(err, files) {
|
226 | files.map(function(f) {
|
227 | return file + "/" + f;
|
228 | }).forEach(traverse);
|
229 | });
|
230 | } else {
|
231 | watch(file);
|
232 | }
|
233 | });
|
234 | };
|
235 | traverse(app_dir);
|
236 | traverse(config_dir);
|
237 | }
|
238 |
|
239 | log("Start at " + (fs.realpathSync(project_root)));
|
240 | registerHandlers();
|
241 | startWorkers();
|
242 | if (do_watch) {
|
243 | startWatch();
|
244 | }
|
245 |
|
246 | process.on('SIGHUP', function() {
|
247 | log("Restart at " + (fs.realpathSync(project_root)));
|
248 | restartWorkers();
|
249 | });
|
250 | process.on('SIGINT', function() {
|
251 | shutdowning = true;
|
252 | exitIfNoWorkers();
|
253 | });
|
254 |
|
255 |
|
256 |
|
257 | setTimeout((function() {}), 1000 * 1000 * 1000);
|