1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | exports.load = function(sg, _) {
|
9 |
|
10 | var path = require('path');
|
11 | var spawn = require('child_process').spawn;
|
12 |
|
13 | var fs = sg.extlibs.fs = require('fs-extra');
|
14 | var glob = sg.extlibs.glob = require('glob');
|
15 |
|
16 | var verbose = sg.verbose;
|
17 |
|
18 | var dogStats;
|
19 |
|
20 | var dogStatsClosure = function() {
|
21 | var server = process.env.SG_STATSD_SERVER || process.env.STATSD_SERVER || process.env.DOG_STATSD_SERVER || 'localhost';
|
22 | try {
|
23 | var dogStatsD = require('dogstatsd').StatsD;
|
24 | dogStats = new dogStatsD(server, 8125);
|
25 | } catch(e) {}
|
26 | };
|
27 | dogStatsClosure();
|
28 |
|
29 | sg.StatsD = function() {
|
30 | var self = this;
|
31 |
|
32 | self.increment = function(name, value, tags) {
|
33 | if (!dogStats) { return; }
|
34 | var args = parseParams.apply(self, arguments);
|
35 |
|
36 | if ('value' in args) { dogStats.incrementBy(args.name, args.value, dogTags(args.tags)); }
|
37 | else { dogStats.increment(args.name, dogTags(args.tags)); }
|
38 | };
|
39 |
|
40 | self.decrement = function(name, value, tags) {
|
41 | if (!dogStats) { return; }
|
42 | var args = parseParams.apply(self, arguments);
|
43 |
|
44 | if ('value' in args) { dogStats.decrementBy(args.name, args.value, dogTags(args.tags)); }
|
45 | else { dogStats.decrement(args.name, dogTags(args.tags)); }
|
46 | };
|
47 |
|
48 | self.gauge = function(name, value, tags) {
|
49 | if (!dogStats) { return; }
|
50 | var args = parseParams.apply(self, arguments);
|
51 |
|
52 | if ('value' in args) { dogStats.gauge(args.name, args.value, dogTags(args.tags)); }
|
53 | else { dogStats.gauge(args.name, dogTags(args.tags)); }
|
54 | };
|
55 |
|
56 | self.histogram = function(name, value, tags) {
|
57 | if (!dogStats) { return; }
|
58 | var args = parseParams.apply(self, arguments);
|
59 |
|
60 | dogStats.histogram(args.name, args.value, dogTags(args.tags));
|
61 | };
|
62 |
|
63 | self.timing = function(name, value, tags) {
|
64 | if (!dogStats) { return; }
|
65 | var args = parseParams.apply(self, arguments);
|
66 |
|
67 | dogStats.timing(args.name, args.value, dogTags(args.tags));
|
68 | };
|
69 |
|
70 | self.set = function(name, value, tags) {
|
71 | if (!dogStats) { return; }
|
72 | var args = parseParams.apply(self, arguments);
|
73 |
|
74 | if ('value' in args) { dogStats.set(args.name, args.value, dogTags(args.tags)); }
|
75 | else { dogStats.set(args.name, dogTags(args.tags)); }
|
76 | };
|
77 |
|
78 | var parseParams = function(name /*, value, tags*/) {
|
79 | var args = _.rest(arguments);
|
80 | var tags = _.isArray(_.last(args)) ? args.pop() : null;
|
81 | var value = args.shift();
|
82 |
|
83 | var result = { name : name };
|
84 | if (value) { result.value = value; }
|
85 | if (tags) { result.tags = tags; }
|
86 |
|
87 | return result;
|
88 | };
|
89 |
|
90 | var dogTags = function(userTags) {
|
91 | var result = [];
|
92 | if (userTags) {
|
93 | result = result.concat(userTags);
|
94 | }
|
95 |
|
96 | _.each(userTags, function(value, key) {
|
97 | if (value === true) {
|
98 | result.push(key);
|
99 | } else {
|
100 | result.push(key + ':' + value);
|
101 | }
|
102 | });
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | return result;
|
117 | };
|
118 | };
|
119 |
|
120 |
|
121 | var stringForHttpCode = function(code) {
|
122 |
|
123 | if (code === 301) { return 'Moved Permanently'; }
|
124 | else if (code === 302) { return 'Found'; }
|
125 | else if (code === 304) { return 'Not Modified'; }
|
126 | else if (code === 400) { return 'Bad Request'; }
|
127 | else if (code === 401) { return 'Unauthorized'; }
|
128 | else if (code === 403) { return 'Permission Denied'; }
|
129 | else if (code === 404) { return 'Not Found'; }
|
130 | else if (code === 418) { return "I'm a teapot"; }
|
131 | else if (code === 429) { return 'Too Many Requests'; }
|
132 | else if (code === 500) { return 'Internal Error'; }
|
133 | else if (code === 501) { return 'Not Implemented'; }
|
134 | else if (code === 521) { return 'Web server is down'; }
|
135 |
|
136 |
|
137 | else if (200 <= code && code < 300) { return 'OK'; }
|
138 | else if (300 <= code && code < 400) { return 'Follow Location'; }
|
139 | else if (400 <= code && code < 500) { return 'Client Error'; }
|
140 | else if (code < 600) { return 'Server Error'; }
|
141 | return '';
|
142 | };
|
143 |
|
144 | var mkResponseObject = function(code, str) {
|
145 |
|
146 | if (code === 301) { return { Location: str}; }
|
147 | else if (code === 302) { return { Location: str}; }
|
148 |
|
149 | return {msg: str};
|
150 | };
|
151 |
|
152 | sg.jsonResponse = function(req, res, code, content_, headers_, debugInfo_) {
|
153 | var content = content_ || {code: code, msg: stringForHttpCode(code)};
|
154 | var headers = headers_ || {};
|
155 | var debugInfo = null;
|
156 |
|
157 | if (process.env.NODE_ENV !== 'production') {
|
158 | if (code >= 400) {
|
159 | debugInfo = debugInfo_ || {};
|
160 | }
|
161 | }
|
162 |
|
163 |
|
164 | if (code === 401 || code === 403) {
|
165 | debugInfo = null;
|
166 | }
|
167 |
|
168 |
|
169 | if (debugInfo_ === null) {
|
170 | debugInfo = null;
|
171 | }
|
172 |
|
173 | if (_.isString(content)) {
|
174 | content = mkResponseObject(code, content);
|
175 | }
|
176 |
|
177 |
|
178 | var origContent = sg.extend(content);
|
179 |
|
180 | if (_.isObject(content)) {
|
181 | if (debugInfo) {
|
182 | content.debug = debugInfo;
|
183 | }
|
184 |
|
185 | content = JSON.stringify(content);
|
186 | }
|
187 |
|
188 | headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
189 | headers['Content-Length'] = content.length;
|
190 |
|
191 | if ((code === 301 || code === 302) && origContent.Location) {
|
192 | headers.Location = origContent.Location;
|
193 | }
|
194 |
|
195 | res.writeHead(code, headers);
|
196 | res.write(content);
|
197 | return res.end();
|
198 | };
|
199 |
|
200 | sg._200 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 200, content, headers, debugInfo); };
|
201 | sg._301 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 301, content, headers, debugInfo); };
|
202 | sg._302 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 302, content, headers, debugInfo); };
|
203 | sg._304 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 304, content, headers, debugInfo); };
|
204 | sg._400 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 400, content, headers, debugInfo); };
|
205 | sg._401 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 401, content, headers, debugInfo); };
|
206 | sg._403 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 403, content, headers, debugInfo); };
|
207 | sg._404 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 404, content, headers, debugInfo); };
|
208 | sg._418 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 418, content, headers, debugInfo); };
|
209 | sg._429 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 429, content, headers, debugInfo); };
|
210 | sg._500 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 500, content, headers, debugInfo); };
|
211 | sg._501 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 501, content, headers, debugInfo); };
|
212 | sg._521 = function(req, res, content, debugInfo, headers) { return sg.jsonResponse(req, res, 521, content, headers, debugInfo); };
|
213 |
|
214 | sg.sendResponseChunks = function(req, res, contentType, chunks) {
|
215 |
|
216 | var headers = {};
|
217 |
|
218 | headers['Content-Type'] = contentType;
|
219 |
|
220 | res.writeHead(200, headers);
|
221 | _.each(chunks, function(chunk) {
|
222 | res.write(chunk);
|
223 | });
|
224 | return res.end();
|
225 | };
|
226 |
|
227 | var findFiles = sg.findFiles = function(pattern, options, callback) {
|
228 | return glob(pattern, options, function(err, filenames_) {
|
229 | if (err) { return callback(err); }
|
230 |
|
231 |
|
232 | var filenames = [];
|
233 | return __eachll(filenames_, function(filename, next) {
|
234 | return fs.stat(filename, function(err, stats) {
|
235 | if (!err && stats.isFile()) {
|
236 | filenames.push(filename);
|
237 | }
|
238 | return next();
|
239 | });
|
240 |
|
241 | }, function(errs) {
|
242 | return callback(null, filenames);
|
243 | });
|
244 | });
|
245 | };
|
246 |
|
247 | var eachLine = sg.eachLine = function(pattern, options_, eachCallback, finalCallback) {
|
248 | var options = _.defaults({}, options_ || {}, {cwd: process.cwd()});
|
249 | var total = 0;
|
250 | var files = [];
|
251 |
|
252 | var eachLineOfOneFile = function(filename, next) {
|
253 | return fs.readFile(filename, 'utf8', function(err, contents) {
|
254 | if (err) { return next(err); }
|
255 |
|
256 | var lines = contents.split('\n');
|
257 | if (options.lineFilter) {
|
258 | lines = _.filter(lines, options.lineFilter);
|
259 | }
|
260 |
|
261 | var i = 0, l = lines.length;
|
262 | var oneLine = function() {
|
263 | total++;
|
264 |
|
265 | var result = eachCallback(lines[i], i, filename, total);
|
266 | if (result === 'SG.nextFile') {
|
267 | return next();
|
268 | }
|
269 |
|
270 | if (result === 'SG.done') {
|
271 | return finalCallback(null, total, files.length);
|
272 | }
|
273 |
|
274 | i += 1;
|
275 | if (i < l) {
|
276 | if (result === 'SG.breathe') {
|
277 |
|
278 | return setTimeout(oneLine, 500);
|
279 | }
|
280 |
|
281 | if (i % 200 === 0) {
|
282 | return process.nextTick(oneLine);
|
283 | }
|
284 |
|
285 | return oneLine();
|
286 | }
|
287 |
|
288 | return next();
|
289 | };
|
290 |
|
291 | return oneLine();
|
292 | });
|
293 | };
|
294 |
|
295 |
|
296 | if (!/\*/.exec(pattern)) {
|
297 |
|
298 | return eachLineOfOneFile(arguments[0], function(err) {
|
299 | return finalCallback(err);
|
300 | });
|
301 | }
|
302 |
|
303 |
|
304 | options.filenameFilter = options.filenameFilter || function(){return true;};
|
305 |
|
306 | return glob(pattern, options, function(err, files_) {
|
307 | if (err) { return finalCallback(err); }
|
308 |
|
309 | files = files_;
|
310 | return sg.__each(files, function(filename_, next) {
|
311 | var filename = path.join(options.cwd || '', filename_);
|
312 |
|
313 | return fs.stat(filename, function(err, stats) {
|
314 | if (err) { return next(); }
|
315 | if (!stats.isFile()) { return next(); }
|
316 |
|
317 | if (!options.filenameFilter(filename)) { return next(); }
|
318 |
|
319 | return eachLineOfOneFile(filename, next);
|
320 | });
|
321 |
|
322 | }, function() {
|
323 | return finalCallback();
|
324 | });
|
325 | });
|
326 | };
|
327 |
|
328 | sg.spawnEz = function(command, args, options) {
|
329 | var stderr = sg.extract(options, 'stderr');
|
330 | var stdout = sg.extract(options, 'stdout');
|
331 | var close = sg.extract(options, 'close');
|
332 | var newline = sg.extract(options, 'newline');
|
333 |
|
334 | var proc, errRemainder = '', outRemainder = '', streamOptions = {};
|
335 |
|
336 |
|
337 | if (_.keys(options).length > 0) {
|
338 | proc = spawn(command, args, options);
|
339 | } else {
|
340 | proc = spawn(command, args);
|
341 | }
|
342 |
|
343 | if (newline) {
|
344 | streamOptions.newline = newline;
|
345 | }
|
346 |
|
347 | if (stderr) {
|
348 | proc.stderr.on('data', function(chunk) {
|
349 | errRemainder = sg.str2lines(errRemainder, chunk, streamOptions, stderr);
|
350 | });
|
351 | }
|
352 |
|
353 | if (stdout) {
|
354 | proc.stdout.on('data', function(chunk) {
|
355 | outRemainder = sg.str2lines(outRemainder, chunk, streamOptions, stdout);
|
356 | });
|
357 | }
|
358 |
|
359 | if (close) {
|
360 | proc.on('close', close);
|
361 | }
|
362 | };
|
363 |
|
364 | |
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 | sg.exec = function(cmd, args /*, options, callback*/) {
|
380 | var args_ = _.rest(arguments, 2);
|
381 | var callback = args_.pop() || lib.noop;
|
382 | var options = args_.pop() || null;
|
383 |
|
384 | var encoding = 'utf8';
|
385 | if (options && 'encoding' in options) {
|
386 | encoding = sg.extract(options, 'encoding');
|
387 | }
|
388 |
|
389 | var proc, error, exitCode, stdoutChunks = [], stderrChunks = [], signal;
|
390 |
|
391 | if (options !== null) {
|
392 | proc = spawn(cmd, args, options);
|
393 | } else {
|
394 | proc = spawn(cmd, args);
|
395 | }
|
396 |
|
397 | proc.stdout.setEncoding(encoding);
|
398 | proc.stdout.on('data', function(chunk) {
|
399 | stdoutChunks.push(chunk);
|
400 | });
|
401 |
|
402 | proc.stderr.setEncoding('utf8');
|
403 | proc.stderr.on('data', function(chunk) {
|
404 | stderrChunks.push(chunk);
|
405 | });
|
406 |
|
407 |
|
408 | var finalFunctionHasBeenCalled = false, closeTimer = null;
|
409 | var finalFunction = function(which) {
|
410 | if (finalFunctionHasBeenCalled) { return; }
|
411 |
|
412 | if (which === 'close') {
|
413 | finalFunctionHasBeenCalled = true;
|
414 | if (closeTimer) {
|
415 | clearTimeout(closeTimer);
|
416 | }
|
417 | return callback(error, exitCode, stdoutChunks, stderrChunks, signal);
|
418 | }
|
419 |
|
420 |
|
421 | return closeTimer = setTimeout(function() {
|
422 | if (finalFunctionHasBeenCalled) { return; }
|
423 | finalFunctionHasBeenCalled = true;
|
424 | return callback(error, exitCode, stdoutChunks, stderrChunks, signal);
|
425 | }, 1000);
|
426 | };
|
427 |
|
428 | proc.on('close', function(exitCode_, signal_) {
|
429 | exitCode = exitCode_;
|
430 | signal = signal_;
|
431 | return finalFunction('close');
|
432 | });
|
433 |
|
434 | proc.on('error', function(err) {
|
435 | error = err;
|
436 | return finalFunction('error');
|
437 | });
|
438 |
|
439 | proc.on('exit', function(exitCode_, signal_) {
|
440 | exitCode = exitCode_;
|
441 | signal = signal_;
|
442 | return finalFunction('exit');
|
443 | });
|
444 |
|
445 | };
|
446 |
|
447 | |
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 | sg.execEz = function(cmd, args /*, options, callback*/) {
|
457 | var args_ = _.rest(arguments, 2);
|
458 | var callback = args_.pop() || lib.noop;
|
459 | var options = args_.pop() || null;
|
460 | var ezOptions = options || {};
|
461 |
|
462 | return sg.exec(cmd, args, options, function(error, exitCode, stdoutChunks, stderrChunks, signal) {
|
463 | var stdout = (stdoutChunks || []).join('').split('\n');
|
464 | var stderr = (stderrChunks || []).join('');
|
465 | var err = {};
|
466 |
|
467 | if (error) { err.error = error; }
|
468 | if (signal) { err.signal = signal; }
|
469 | if (exitCode !== 0) { err.exitCode = exitCode; }
|
470 | if (stderr.length > 0) { err.stderr = stderr; }
|
471 |
|
472 | if (sg.numKeys(err) !== 0) {
|
473 | if (!ezOptions.quiet) {
|
474 | sg.reportOutput(cmd+args.join(" "), error, exitCode, stdoutChunks, stderrChunks, signal);
|
475 | }
|
476 | } else {
|
477 | err = null;
|
478 | }
|
479 |
|
480 | return callback(err, stdout);
|
481 | });
|
482 | };
|
483 |
|
484 | |
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 | sg.reportOutput = function(msg_, error, exitCode, stdoutChunks, stderrChunks, signal) {
|
499 |
|
500 | const stdoutLines = _.compact(stdoutChunks.join('').split('\n'));
|
501 |
|
502 | if (msg_ !== null) {
|
503 | const msg = sg.lpad(msg_+':', 50);
|
504 | const stderrLines = _.compact(stderrChunks.join('').split('\n'));
|
505 |
|
506 |
|
507 | if (stdoutLines.length === 1) {
|
508 | console.log(`${msg} exit: ${exitCode}, SIGNAL: ${signal}: ${stdoutLines[0]}`);
|
509 | } else {
|
510 | console.log(`${msg} exit: ${exitCode}, SIGNAL: ${signal}`);
|
511 | }
|
512 |
|
513 |
|
514 | if (error) {
|
515 | console.error(error);
|
516 | }
|
517 |
|
518 |
|
519 | if (stdoutLines.length === 1) {
|
520 | } else {
|
521 | _.each(stdoutLines, line => {
|
522 | console.log(`${msg} ${line}`);
|
523 | });
|
524 | }
|
525 |
|
526 |
|
527 | const stderr = stderrChunks.join('');
|
528 | if (stderr.length > 0) {
|
529 | _.each(stderrLines, line => {
|
530 | console.error(`${msg} ${line}`);
|
531 | });
|
532 | }
|
533 | }
|
534 |
|
535 | var err = (exitCode !== 0 ? `NONZEROEXIT:${exitCode}` : (signal ? `SIG${signal}` : error));
|
536 | return [err, stdoutLines];
|
537 | };
|
538 |
|
539 | |
540 |
|
541 |
|
542 | sg.getMacAddress = function(callback) {
|
543 | if (sg.macAddress_) { return callback(null, sg.macAddress_); }
|
544 |
|
545 | var currInterface, ifaceStats, currIfaceStats = {}, m, value;
|
546 | return sg.execEz('ifconfig', [], {}, function(err, lines) {
|
547 | _.each(lines, function(line, lineNum) {
|
548 | if ((m = line.match(/^([a-z0-9]+):\s*(.*)$/i))) {
|
549 |
|
550 | if (currInterface) {
|
551 |
|
552 | analyzeStats();
|
553 | }
|
554 |
|
555 | currInterface = m[1];
|
556 | currIfaceStats = {ifaceName: currInterface};
|
557 | line = m[2];
|
558 | }
|
559 |
|
560 |
|
561 | if ((m = line.match(/([a-z0-9_]+)(:|=)?\s*(.*)$/i))) {
|
562 | value = m[3];
|
563 | if (m[1] === 'ether') {
|
564 | value = value.replace(/\s*/g, '');
|
565 | }
|
566 | currIfaceStats[m[1]] = value;
|
567 | }
|
568 | });
|
569 |
|
570 | analyzeStats();
|
571 | sg.macAddress_ = ifaceStats.ether || sg.macAddress_;
|
572 |
|
573 | return callback(null, sg.macAddress_);
|
574 | });
|
575 |
|
576 | function analyzeStats() {
|
577 | if (currIfaceStats.status !== 'active') { return; }
|
578 | if (!currIfaceStats.inet) { return; }
|
579 | if (!currIfaceStats.ether.match(/^([a-f0-9]{2}[:]){5}[a-f0-9]{2}$/i)) { return; }
|
580 |
|
581 |
|
582 | if (!ifaceStats) {
|
583 | ifaceStats = sg.deepCopy(currIfaceStats);
|
584 | } else if (currIfaceStats.ether > ifaceStats.ether) {
|
585 | ifaceStats = sg.deepCopy(currIfaceStats);
|
586 | }
|
587 | }
|
588 | };
|
589 | sg.macAddress_ = null;
|
590 |
|
591 | |
592 |
|
593 |
|
594 | sg.getHardwareId = function(callback) {
|
595 | return sg.getMacAddress(callback);
|
596 | };
|
597 |
|
598 | |
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 | sg.writeChunks = function(path, chunks, callback) {
|
607 |
|
608 | var stream = fs.createWriteStream(path);
|
609 | var i = 0;
|
610 |
|
611 |
|
612 | write();
|
613 | function write() {
|
614 | var ok = true;
|
615 | do {
|
616 |
|
617 | if (i < chunks.length - 1) {
|
618 |
|
619 |
|
620 | ok = stream.write(chunks[i]);
|
621 | } else {
|
622 |
|
623 | stream.write(chunks[i], callback);
|
624 | }
|
625 |
|
626 | i += 1;
|
627 | } while (i < chunks.length && ok);
|
628 |
|
629 | if (i < chunks.length) {
|
630 |
|
631 |
|
632 | stream.once('drain', write);
|
633 | }
|
634 | }
|
635 | };
|
636 |
|
637 | var defAttr = function(obj, key, def_) {
|
638 | var defValue = def_ || {};
|
639 | obj[key] = obj[key] || defValue;
|
640 | return obj[key];
|
641 | };
|
642 |
|
643 | var incAttr = function(obj, key) {
|
644 | obj[key] = (obj[key] || 0) + 1;
|
645 | return obj[key];
|
646 | };
|
647 |
|
648 | sg.DataHouse = function() {
|
649 | var self = this;
|
650 | self.data = {};
|
651 |
|
652 | self.multiKeyCount = function(key, value, mfg) {
|
653 | var args = _.toArray(arguments);
|
654 | var last = args.pop();
|
655 | var obj = self.data;
|
656 |
|
657 | _.each(args, function(arg) {
|
658 | obj = defAttr(obj, arg);
|
659 | });
|
660 |
|
661 | return incAttr(obj, last);
|
662 | };
|
663 | };
|
664 |
|
665 |
|
666 |
|
667 | return sg;
|
668 | };
|