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() }; } 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; let match = name.match(/^(.+)_main$/); 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}` }); } } // 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`; let configString = template({info, services}); 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(); } 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('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; timeoutId = setTimeout(async () => { timeoutId = null; await updateProxy(); }, conf.delay * 1000); }) }); })();