1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var vizion = require('vizion');
|
7 | var cst = require('../constants.js');
|
8 | var eachLimit = require('async/eachLimit');
|
9 | var debug = require('debug')('pm2:worker');
|
10 | var domain = require('domain');
|
11 | var cronJob = require('cron').CronJob
|
12 | var vCheck = require('./VersionCheck.js')
|
13 | var pkg = require('../package.json')
|
14 |
|
15 | module.exports = function(God) {
|
16 | var timer = null;
|
17 |
|
18 | God.CronJobs = new Map();
|
19 | God.Worker = {};
|
20 | God.Worker.is_running = false;
|
21 |
|
22 | God.getCronID = function(pm_id) {
|
23 | return `cron-${pm_id}`
|
24 | }
|
25 |
|
26 | God.registerCron = function(pm2_env) {
|
27 | if (!pm2_env ||
|
28 | pm2_env.pm_id === undefined ||
|
29 | !pm2_env.cron_restart ||
|
30 | God.CronJobs.has(God.getCronID(pm2_env.pm_id)))
|
31 | return;
|
32 |
|
33 | var pm_id = pm2_env.pm_id
|
34 | console.log('[PM2][WORKER] Registering a cron job on:', pm_id);
|
35 |
|
36 | var job = new cronJob({
|
37 | cronTime: pm2_env.cron_restart,
|
38 | onTick: function() {
|
39 | God.restartProcessId({id: pm_id}, function(err, data) {
|
40 | if (err)
|
41 | console.error(err.stack || err);
|
42 | return;
|
43 | });
|
44 | },
|
45 | start: false
|
46 | });
|
47 |
|
48 | job.start();
|
49 | God.CronJobs.set(God.getCronID(pm_id), job);
|
50 | }
|
51 |
|
52 |
|
53 | |
54 |
|
55 |
|
56 | God.deleteCron = function(id) {
|
57 | if (typeof(id) !== 'undefined' && God.CronJobs.has(God.getCronID(id)) === false)
|
58 | return;
|
59 | console.log('[PM2] Deregistering a cron job on:', id);
|
60 | var job = God.CronJobs.get(God.getCronID(id));
|
61 | job.stop();
|
62 | God.CronJobs.delete(God.getCronID(id));
|
63 | };
|
64 |
|
65 | var _getProcessById = function(pm_id) {
|
66 | var proc = God.clusters_db[pm_id];
|
67 | return proc ? proc : null;
|
68 | };
|
69 |
|
70 |
|
71 | var maxMemoryRestart = function(proc_key, cb) {
|
72 | var proc = _getProcessById(proc_key.pm2_env.pm_id);
|
73 |
|
74 | if (!(proc &&
|
75 | proc.pm2_env &&
|
76 | proc_key.monit))
|
77 | return cb();
|
78 |
|
79 | if (proc_key.monit.memory !== undefined &&
|
80 | proc.pm2_env.max_memory_restart !== undefined &&
|
81 | proc.pm2_env.max_memory_restart < proc_key.monit.memory &&
|
82 | proc.pm2_env.axm_options &&
|
83 | proc.pm2_env.axm_options.pid === undefined) {
|
84 | console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value (current_memory=%s max_memory_limit=%s [octets])', proc.pm2_env.pm_id, proc_key.monit.memory, proc.pm2_env.max_memory_restart);
|
85 | God.reloadProcessId({
|
86 | id : proc.pm2_env.pm_id
|
87 | }, function(err, data) {
|
88 | if (err)
|
89 | console.error(err.stack || err);
|
90 | return cb();
|
91 | });
|
92 | }
|
93 | else {
|
94 | return cb();
|
95 | }
|
96 | };
|
97 |
|
98 |
|
99 | var versioningRefresh = function(proc_key, cb) {
|
100 | var proc = _getProcessById(proc_key.pm2_env.pm_id);
|
101 | if (!(proc &&
|
102 | proc.pm2_env &&
|
103 | (proc.pm2_env.vizion !== false && proc.pm2_env.vizion != "false") &&
|
104 | proc.pm2_env.versioning &&
|
105 | proc.pm2_env.versioning.repo_path)) {
|
106 | return cb();
|
107 | }
|
108 |
|
109 | if (proc.pm2_env.vizion_running === true)
|
110 | {
|
111 | debug('Vizion is already running for proc id: %d, skipping this round', proc.pm2_env.pm_id);
|
112 | return cb();
|
113 | }
|
114 |
|
115 | proc.pm2_env.vizion_running = true;
|
116 | var repo_path = proc.pm2_env.versioning.repo_path;
|
117 |
|
118 | vizion.analyze({
|
119 | folder: proc.pm2_env.versioning.repo_path
|
120 | },
|
121 | function(err, meta) {
|
122 | if (err != null)
|
123 | return cb();
|
124 |
|
125 | proc = _getProcessById(proc_key.pm2_env.pm_id);
|
126 |
|
127 | if (!(proc &&
|
128 | proc.pm2_env &&
|
129 | proc.pm2_env.versioning &&
|
130 | proc.pm2_env.versioning.repo_path)) {
|
131 | console.error('Proc not defined anymore or versioning unknown');
|
132 | return cb();
|
133 | }
|
134 |
|
135 | proc.pm2_env.vizion_running = false;
|
136 | meta.repo_path = repo_path;
|
137 | proc.pm2_env.versioning = meta;
|
138 | debug('[PM2][WORKER] %s parsed for versioning', proc.pm2_env.name);
|
139 | return cb();
|
140 | });
|
141 | };
|
142 |
|
143 | var tasks = function() {
|
144 | if (God.Worker.is_running === true) {
|
145 | debug('[PM2][WORKER] Worker is already running, skipping this round');
|
146 | return false;
|
147 | }
|
148 | God.Worker.is_running = true;
|
149 |
|
150 | God.getMonitorData(null, function(err, data) {
|
151 | if (err || !data || typeof(data) !== 'object') {
|
152 | God.Worker.is_running = false;
|
153 | return console.error(err);
|
154 | }
|
155 |
|
156 | eachLimit(data, 1, function(proc, next) {
|
157 | if (!proc || !proc.pm2_env || proc.pm2_env.pm_id === undefined)
|
158 | return next();
|
159 |
|
160 | debug('[PM2][WORKER] Processing proc id:', proc.pm2_env.pm_id);
|
161 |
|
162 |
|
163 | if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
|
164 | proc.pm2_env.prev_restart_delay && proc.pm2_env.prev_restart_delay > 0) {
|
165 | var app_uptime = Date.now() - proc.pm2_env.pm_uptime
|
166 | if (app_uptime > cst.EXP_BACKOFF_RESET_TIMER) {
|
167 | var ref_proc = _getProcessById(proc.pm2_env.pm_id);
|
168 | ref_proc.pm2_env.prev_restart_delay = 0
|
169 | console.log(`[PM2][WORKER] Reset the restart delay, as app ${proc.name} has been up for more than ${cst.EXP_BACKOFF_RESET_TIMER}ms`)
|
170 | }
|
171 | }
|
172 |
|
173 |
|
174 | maxMemoryRestart(proc, function() {
|
175 | return next();
|
176 | });
|
177 | }, function(err) {
|
178 | God.Worker.is_running = false;
|
179 | debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000));
|
180 | });
|
181 | });
|
182 | };
|
183 |
|
184 | var wrappedTasks = function() {
|
185 | var d = domain.create();
|
186 |
|
187 | d.once('error', function(err) {
|
188 | console.error('[PM2][WORKER] Error caught by domain:\n' + (err.stack || err));
|
189 | God.Worker.is_running = false;
|
190 | });
|
191 |
|
192 | d.run(function() {
|
193 | tasks();
|
194 | });
|
195 | };
|
196 |
|
197 |
|
198 | God.Worker.start = function() {
|
199 | timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL);
|
200 |
|
201 | setInterval(() => {
|
202 | vCheck({
|
203 | state: 'check',
|
204 | version: pkg.version
|
205 | })
|
206 | }, 1000 * 60 * 60 * 24)
|
207 | };
|
208 |
|
209 | God.Worker.stop = function() {
|
210 | if (timer !== null)
|
211 | clearInterval(timer);
|
212 | };
|
213 | };
|