This repository has been archived on 2024-07-12. You can view files and clone it, but cannot push or open issues or pull requests.
docker-discover/index.js

159 lines
4.1 KiB
JavaScript

require('require-yaml');
let Docker = require('dockerode');
let handlebars = require('handlebars');
let ssh = require('node-ssh');
let fs = require('fs');
let shajs = require('sha.js');
let conf = require('./config.yml');
let package = require('./package.json');
let docker;
let template;
let lastInfoHash;
let appName = package.name;
let isProduction = process.env.NODE_ENV === 'production';
let tmpDir = isProduction ? `/tmp/${appName}` : `${__dirname}/tmp`;
let hashFile = `${tmpDir}/config.hash`;
async function updateProxy() {
console.log('Updating reverse proxy configuration.');
// Obtaining Docker settings
let info;
if (!isProduction) {
info = require('./test.json');
} else {
info = {
services: await docker.listServices(),
nodes: await docker.listNodes()
};
}
function sortFn(a, b) {
return a.name > b.name ? 1 : a.name < b.name ? -1 : 0;
}
let nodes = [];
for (let node of info.nodes) {
let address = node.ManagerStatus
? node.ManagerStatus.Addr.split(':')[0]
: node.Status.Addr;
nodes.push({
name: node.Description.Hostname,
address
});
}
nodes = nodes.sort(sortFn);
let services = [];
for (let service of info.services) {
let ports = service.Endpoint.Ports;
if (!Array.isArray(ports) || !ports.length) continue;
let name = service.Spec.Name;
let match = name.match(/^(.+)_main$/);
if (match) name = match[1];
services.push({
name,
port: ports[0].PublishedPort
});
}
services = services.sort(sortFn);
let configString = template({services, nodes, info});
// Cheking settings hash
let infoHash = shajs('sha256')
.update(configString)
.digest('hex');
console.log('Settings hash:', infoHash);
if (lastInfoHash == infoHash) {
console.log(`Settings haven't changed, aborting.`);
return;
}
// Creating configuration file
let tmpConf = `${tmpDir}/config.cfg`;
fs.writeFileSync(tmpConf, configString);
if (conf.debug) {
let delimiter = '#'.repeat(80);
console.log(delimiter);
console.log(configString);
console.log(delimiter);
}
// Updating reverse proxies
let files = {
local: tmpConf,
remote: conf.rproxy.confPath
};
for (let host of conf.rproxy.hosts) {
console.log(`Updating host: ${host}`);
if (!isProduction) continue;
let sshClient = new ssh();
await sshClient.connect(Object.assign({host}, conf.rproxy.auth));
await sshClient.putFiles([files]);
if (conf.rproxy.reloadCmd)
await sshClient.exec(conf.rproxy.reloadCmd);
await sshClient.dispose();
}
// Saving applied config hash
lastInfoHash = infoHash;
fs.writeFileSync(hashFile, infoHash);
console.log('Configuration updated.');
}
(async() => {
console.log('Initializing.');
let timeoutId;
docker = new Docker(conf.docker);
template = handlebars.compile(fs.readFileSync('rproxy.handlebars', 'utf8'));
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);
}
await updateProxy();
console.log('Listening 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;
timeoutId = setTimeout(async () => {
timeoutId = null;
await updateProxy();
}, conf.delay * 1000);
})
});
})();