[MediaWiki-commits] [Gerrit] Periodically check for configuration file changes. - change (mediawiki...OfflineContentGenerator)

2016-04-26 Thread jenkins-bot (Code Review)
jenkins-bot has submitted this change and it was merged.

Change subject: Periodically check for configuration file changes.
..


Periodically check for configuration file changes.

This allows a service to gracefully restart when its configuration changes.
If configuration files are maintained (eg, with puppet) and this
service is automatically restarted (eg, with upstart), this saves an explicit
restart step when deploying configuration changes.  When the
`config.coordinator.checkTime` variable is 0 (which is the default), this
automatic check is disabled and you will have to manually restart the service
(by sending SIGINT, SIGTERM, or SIGHUP) after the configuration is changed.

Also change SIGHUP handler to graceful shutdown, since Unix convention is
to use SIGUP to signal configuration file changes.

Change-Id: I586c2c39b8efcacbd50e884374aaeae2890b4bb9
---
M defaults.js
M lib/cli.js
M mw-ocg-service.js
3 files changed, 61 insertions(+), 16 deletions(-)

Approvals:
  Cscott: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/defaults.js b/defaults.js
index edfc8ed..a97637a 100644
--- a/defaults.js
+++ b/defaults.js
@@ -23,6 +23,12 @@
 
/** Public hostname of this instance for HTTP GET requests for 
locally stored content. */
hostname: null,
+
+   /** How often (in seconds) to check for configuration changes.
+* We gracefully shutdown when we get a config change; 
presumably
+* upstart or some other service runner will restart us.  
Defaults
+* to zero which means, "never check for configuration 
changes". */
+   checkTime: 0,
},
/** Configuration for the frontend HTTP server thread. You can choose 
to serve
 * content via a local socket, or an IP address. If both are null the 
server will
diff --git a/lib/cli.js b/lib/cli.js
index 7972cdb..9fb0c88 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -18,9 +18,15 @@
 
 // Parse configuration files, with optional command-line override.
 var parseConfig = exports.parseConfig = function(commanderConfig) {
+   var deps = [], latest = 0, req = function(f) {
+   f = path.resolve(__dirname, f);
+   deps.push('+' + f); // Record filenames for all config files 
parsed.
+   latest = Math.max(latest, fs.statSync(f).ctime.getTime());
+   return require(f);
+   };
 
/* === Configuration Options & File === */
-   var config = require('../defaults.js'), configPath = '..';
+   var config = req('../defaults.js'), configPath = '..';
// Local configuration overrides.
while (config.config) {
var configFile = relativeTo(configPath, config.config);
@@ -28,9 +34,10 @@
try {
fs.statSync(configFile);
} catch (e) {
+   deps.push('-' + configFile);
break; // File not present.
}
-   config = require(configFile)(config) || config;
+   config = req(configFile)(config) || config;
configPath = path.dirname(configFile);
}
 
@@ -41,7 +48,7 @@
// to the current working directory instead of relative 
to the path of this
// file.
commanderConfig = relativeTo(process.cwd(), 
commanderConfig);
-   config = require(commanderConfig)(config) || config;
+   config = req(commanderConfig)(config) || config;
}
} catch (err) {
console.error('Could not open configuration file %s! %s', 
commanderConfig, err);
@@ -54,9 +61,38 @@
"Failed jobs shouldn't be gc'd before the lockout time expires."
);
 
+   // Record all dependent files and the latest ctime.
+   config.config_deps = deps;
+   config.config_time = latest;
return config;
 };
 
+// Check to see if any configuration files have changed.
+var configChanged = exports.configChanged = function(config) {
+   var deps = config.config_deps, latest = config.config_time, i;
+   for (i = 0; i < deps.length; i++) {
+   var s;
+   try {
+   s = fs.statSync(deps[i].slice(1));
+   } catch (e) {
+   s = null;
+   }
+   if (deps[i][0] === '+') {
+   if (s === null || s.ctime.getTime() > latest) {
+   // Newer file, or an expected file disappeared.
+   return true;
+   }
+   } else {
+   if (s !== null) {
+   // A file appeared!
+   return true;
+   }
+   }
+   }
+   // No changes.

[MediaWiki-commits] [Gerrit] Periodically check for configuration file changes. - change (mediawiki...OfflineContentGenerator)

2016-04-20 Thread Cscott (Code Review)
Cscott has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/284599

Change subject: Periodically check for configuration file changes.
..

Periodically check for configuration file changes.

This allows a service to gracefully restart when its configuration changes.

Change-Id: I586c2c39b8efcacbd50e884374aaeae2890b4bb9
---
M defaults.js
M lib/cli.js
M mw-ocg-service.js
3 files changed, 61 insertions(+), 3 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Collection/OfflineContentGenerator
 refs/changes/99/284599/1

diff --git a/defaults.js b/defaults.js
index edfc8ed..a97637a 100644
--- a/defaults.js
+++ b/defaults.js
@@ -23,6 +23,12 @@
 
/** Public hostname of this instance for HTTP GET requests for 
locally stored content. */
hostname: null,
+
+   /** How often (in seconds) to check for configuration changes.
+* We gracefully shutdown when we get a config change; 
presumably
+* upstart or some other service runner will restart us.  
Defaults
+* to zero which means, "never check for configuration 
changes". */
+   checkTime: 0,
},
/** Configuration for the frontend HTTP server thread. You can choose 
to serve
 * content via a local socket, or an IP address. If both are null the 
server will
diff --git a/lib/cli.js b/lib/cli.js
index 7972cdb..77d07a8 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -18,9 +18,15 @@
 
 // Parse configuration files, with optional command-line override.
 var parseConfig = exports.parseConfig = function(commanderConfig) {
+   var deps = [], latest = 0, req = function(f) {
+   f = path.resolve(__dirname, f);
+   deps.push('+' + f); // record filenames for all config files 
parsed.
+   latest = Math.max(latest, fs.statSync(f).ctime.getTime());
+   return require(f);
+   };
 
/* === Configuration Options & File === */
-   var config = require('../defaults.js'), configPath = '..';
+   var config = req('../defaults.js'), configPath = '..';
// Local configuration overrides.
while (config.config) {
var configFile = relativeTo(configPath, config.config);
@@ -28,9 +34,10 @@
try {
fs.statSync(configFile);
} catch (e) {
+   deps.push('-' + configFile);
break; // File not present.
}
-   config = require(configFile)(config) || config;
+   config = req(configFile)(config) || config;
configPath = path.dirname(configFile);
}
 
@@ -41,7 +48,7 @@
// to the current working directory instead of relative 
to the path of this
// file.
commanderConfig = relativeTo(process.cwd(), 
commanderConfig);
-   config = require(commanderConfig)(config) || config;
+   config = req(commanderConfig)(config) || config;
}
} catch (err) {
console.error('Could not open configuration file %s! %s', 
commanderConfig, err);
@@ -54,9 +61,38 @@
"Failed jobs shouldn't be gc'd before the lockout time expires."
);
 
+   // Record all dependent files and the latest ctime.
+   config.config_deps = deps;
+   config.config_time = latest;
return config;
 };
 
+// Check to see if any configuration files have changed.
+var configChanged = exports.configChanged = function(config) {
+   var deps = config.config_deps, latest = config.config_time, i;
+   for (i=0; i latest) {
+   // Newer file, or an expected file disappeared.
+   return true;
+   }
+   } else {
+   if (s!==null) {
+   // A file appeared!
+   return true;
+   }
+   }
+   }
+   // No changes.
+   return false;
+};
+
 // Set up logging.
 var setupLogging = exports.setupLogging = function(config, forceStdout) {
var bunyan = require('bunyan');
diff --git a/mw-ocg-service.js b/mw-ocg-service.js
index 9f99d66..e140ee6 100755
--- a/mw-ocg-service.js
+++ b/mw-ocg-service.js
@@ -71,8 +71,10 @@
var i;
 
/* --- Thread management --- */
+   var checkTimer = 0;
var gracefulShutdown = function gracefulShutdown() {
respawnWorkers