UNPKG

6.37 kBJavaScriptView Raw
1var j, len, logstream, openLogFile, ref, root;
2
3const cluster = require('cluster');
4const domain = require('domain');
5const fs = require('fs');
6const path = require('path');
7const yaml = require('js-yaml');
8
9let project_root = process.env.PROJECT_ROOT;
10if (/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}
13project_root = path.relative(process.cwd(), project_root);
14if (!project_root) {
15 project_root = '.';
16}
17
18const app_dir = project_root + '/app';
19const config_dir = project_root + '/config';
20
21let shutdowning = false;
22
23const config = yaml.safeLoad(fs.readFileSync(project_root + '/deploy.yaml', 'utf-8'));
24let do_watch = false;
25let devel_mode = false;
26let redirect_log = false;
27
28ref = process.argv;
29for (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
42if (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
70function log(msg) {
71 console.log("[" + (Date.now()) + "] [deployer] " + msg);
72}
73
74function debug(msg) {
75 // console.log(msg);
76}
77
78function exitIfNoWorkers() {
79 if (Object.keys(cluster.workers).length === 0) {
80 console.log("[" + (Date.now()) + "] [deployer] Terminate");
81 process.exit(0);
82 }
83}
84
85function 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
101function 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
129function 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
160function 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 // worker가 정상적으로 뜨지 않은 경우 그냥 시작한다
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 // fork에 문제가 생긴 경우 kill 과정을 중단한다
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
198function 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 // debug('watching... ' + file.substr(project_root.length+1));
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
239log("Start at " + (fs.realpathSync(project_root)));
240registerHandlers();
241startWorkers();
242if (do_watch) {
243 startWatch();
244}
245
246process.on('SIGHUP', function() {
247 log("Restart at " + (fs.realpathSync(project_root)));
248 restartWorkers();
249});
250process.on('SIGINT', function() {
251 shutdowning = true;
252 exitIfNoWorkers();
253});
254
255// server.js가 그냥 종료되면 forever가 다시 띄운다
256// 혹시 worker에 문제가 있어서 실행이 안 되도 server.js는 유지되게 한다
257setTimeout((function() {}), 1000 * 1000 * 1000);