2020-01-28 13:57:24 +00:00
|
|
|
require('require-yaml');
|
2020-01-27 16:25:39 +00:00
|
|
|
let Docker = require('dockerode');
|
|
|
|
let handlebars = require('handlebars');
|
|
|
|
let ssh = require('node-ssh');
|
|
|
|
let fs = require('fs');
|
2020-01-29 12:06:54 +00:00
|
|
|
let shajs = require('sha.js');
|
2020-01-28 13:57:24 +00:00
|
|
|
let conf = require('./config.yml');
|
2020-01-29 12:06:54 +00:00
|
|
|
let package = require('./package.json');
|
2020-01-27 16:25:39 +00:00
|
|
|
|
|
|
|
let docker;
|
|
|
|
let template;
|
2020-01-29 12:06:54 +00:00
|
|
|
let lastInfoHash;
|
|
|
|
let appName = package.name;
|
2020-01-28 13:57:24 +00:00
|
|
|
let isProduction = process.env.NODE_ENV === 'production';
|
2020-01-29 12:06:54 +00:00
|
|
|
let tmpDir = isProduction ? `/tmp/${appName}` : `${__dirname}/tmp`;
|
|
|
|
let hashFile = `${tmpDir}/config.hash`;
|
2020-01-27 16:25:39 +00:00
|
|
|
|
|
|
|
async function updateProxy() {
|
2020-01-28 13:57:24 +00:00
|
|
|
console.log('Updating reverse proxy configuration.');
|
2020-01-29 12:06:54 +00:00
|
|
|
|
|
|
|
// Obtaining Docker settings
|
|
|
|
|
2020-01-28 13:57:24 +00:00
|
|
|
let info;
|
2020-01-27 16:25:39 +00:00
|
|
|
|
2020-01-28 13:57:24 +00:00
|
|
|
if (!isProduction) {
|
|
|
|
info = require('./test.json');
|
2020-01-27 16:25:39 +00:00
|
|
|
} else {
|
2020-01-28 13:57:24 +00:00
|
|
|
info = {
|
2020-01-27 16:25:39 +00:00
|
|
|
services: await docker.listServices(),
|
|
|
|
nodes: await docker.listNodes()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-01-28 13:57:24 +00:00
|
|
|
let services = [];
|
|
|
|
for (let serviceInfo of info.services) {
|
|
|
|
let ports = serviceInfo.Endpoint.Ports;
|
|
|
|
if (!Array.isArray(ports) || !ports.length) continue;
|
|
|
|
|
|
|
|
let name = serviceInfo.Spec.Name;
|
2020-01-29 12:06:54 +00:00
|
|
|
let match = name.match(/^(.+)_main$/);
|
2020-01-28 13:57:24 +00:00
|
|
|
if (match) name = match[1];
|
|
|
|
|
|
|
|
let service = {
|
|
|
|
name,
|
|
|
|
port: ports[0].PublishedPort,
|
|
|
|
nodes: []
|
|
|
|
};
|
|
|
|
services.push(service);
|
|
|
|
|
|
|
|
for (let node of info.nodes) {
|
|
|
|
let address = node.ManagerStatus
|
|
|
|
? node.ManagerStatus.Addr.split(':')[0]
|
|
|
|
: node.Status.Addr;
|
|
|
|
|
|
|
|
service.nodes.push({
|
|
|
|
name: node.Description.Hostname,
|
|
|
|
endpoint: `${address}:${service.port}`
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-29 12:06:54 +00:00
|
|
|
// Cheking settings hash
|
|
|
|
|
|
|
|
let infoHash = shajs('sha256')
|
|
|
|
.update(JSON.stringify(services))
|
|
|
|
.digest('hex');
|
|
|
|
console.log('Settings hash:', infoHash);
|
|
|
|
|
|
|
|
if (lastInfoHash == infoHash) {
|
|
|
|
console.log(`Settings haven't changed, aborting.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastInfoHash = infoHash;
|
|
|
|
fs.writeFileSync(hashFile, infoHash);
|
|
|
|
|
|
|
|
// Creating configuration file
|
|
|
|
|
|
|
|
let tmpConf = `${tmpDir}/config.cfg`;
|
2020-01-28 13:57:24 +00:00
|
|
|
let configString = template({info, services});
|
2020-01-27 16:25:39 +00:00
|
|
|
fs.writeFileSync(tmpConf, configString);
|
|
|
|
|
2020-01-29 12:06:54 +00:00
|
|
|
if (conf.debug) {
|
2020-01-27 16:25:39 +00:00
|
|
|
let delimiter = '#'.repeat(80);
|
|
|
|
console.log(delimiter);
|
|
|
|
console.log(configString);
|
|
|
|
console.log(delimiter);
|
|
|
|
}
|
|
|
|
|
2020-01-29 12:06:54 +00:00
|
|
|
// Updating reverse proxies
|
|
|
|
|
2020-01-27 16:25:39 +00:00
|
|
|
let files = {
|
|
|
|
local: tmpConf,
|
2020-01-28 13:57:24 +00:00
|
|
|
remote: conf.rproxy.confPath
|
2020-01-27 16:25:39 +00:00
|
|
|
};
|
|
|
|
|
2020-01-28 13:57:24 +00:00
|
|
|
for (let host of conf.rproxy.hosts) {
|
2020-01-27 16:25:39 +00:00
|
|
|
console.log(`Updating host: ${host}`);
|
2020-01-28 13:57:24 +00:00
|
|
|
if (!isProduction) continue;
|
|
|
|
|
2020-01-27 16:25:39 +00:00
|
|
|
let sshClient = new ssh();
|
2020-01-28 13:57:24 +00:00
|
|
|
await sshClient.connect(Object.assign({host}, conf.rproxy.auth));
|
2020-01-27 16:25:39 +00:00
|
|
|
await sshClient.putFiles([files]);
|
2020-01-28 13:57:24 +00:00
|
|
|
if (conf.rproxy.reloadCmd)
|
2020-01-28 15:43:43 +00:00
|
|
|
await sshClient.exec(conf.rproxy.reloadCmd);
|
2020-01-27 16:25:39 +00:00
|
|
|
await sshClient.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('Configuration updated.');
|
|
|
|
}
|
|
|
|
|
|
|
|
(async() => {
|
2020-01-29 12:06:54 +00:00
|
|
|
console.log('Initializing.');
|
2020-01-27 16:25:39 +00:00
|
|
|
let timeoutId;
|
|
|
|
docker = new Docker(conf.docker);
|
|
|
|
template = handlebars.compile(fs.readFileSync('rproxy.handlebars', 'utf8'));
|
2020-01-29 12:06:54 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
fs.mkdirSync(tmpDir);
|
|
|
|
} catch (err) {
|
|
|
|
if (err.code != 'EEXIST') throw err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs.existsSync(hashFile)) {
|
|
|
|
lastInfoHash = fs.readFileSync(hashFile, 'utf8');
|
|
|
|
console.log('Saved settings hash:', lastInfoHash);
|
|
|
|
}
|
|
|
|
|
2020-01-28 13:57:24 +00:00
|
|
|
await updateProxy();
|
2020-01-27 16:25:39 +00:00
|
|
|
|
|
|
|
console.log('Listenig for events.')
|
|
|
|
docker.getEvents({}, (err, stream) => {
|
|
|
|
if (err || !stream) {
|
|
|
|
console.error('Failed to monitor docker host', err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.on('data', event => {
|
|
|
|
event = JSON.parse(event);
|
|
|
|
if (conf.events && conf.events.indexOf(event.Type) == -1) return;
|
|
|
|
console.log(`Event: ${event.Type}: ${event.Action}`);
|
|
|
|
|
|
|
|
if (timeoutId) return;
|
2020-01-28 13:57:24 +00:00
|
|
|
timeoutId = setTimeout(async () => {
|
2020-01-27 16:25:39 +00:00
|
|
|
timeoutId = null;
|
2020-01-28 13:57:24 +00:00
|
|
|
await updateProxy();
|
2020-01-27 16:25:39 +00:00
|
|
|
}, conf.delay * 1000);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
})();
|