firmament-docker
Version:
Typescript classes for performing Docker operations
872 lines (863 loc) • 41.1 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
var DockerProvisionImpl_1;
const inversify_1 = require("inversify");
const firmament_yargs_1 = require("firmament-yargs");
const async = require("async");
const fs = require("fs");
const YAML = require("yamljs");
const path = require("path");
const tmp = require("tmp");
const mkdirp = require("mkdirp");
const touch = require("touch");
const fileExists = require('file-exists');
let DockerProvisionImpl = DockerProvisionImpl_1 = class DockerProvisionImpl extends firmament_yargs_1.ForceErrorImpl {
constructor(commandUtil, spawn, safeJson, remoteCatalogGetter, processCommandJson, dockerUtil, dockerImageManagement, dockerContainerManagement, positive, progressBar) {
super();
this.commandUtil = commandUtil;
this.spawn = spawn;
this.safeJson = safeJson;
this.remoteCatalogGetter = remoteCatalogGetter;
this.processCommandJson = processCommandJson;
this.dockerUtil = dockerUtil;
this.dockerImageManagement = dockerImageManagement;
this.dockerContainerManagement = dockerContainerManagement;
this.positive = positive;
this.progressBar = progressBar;
this.writeScripts = false;
}
validateDockerStackConfigTemplate(argv, dockerStackConfigTemplate, cb) {
const me = this;
const dsct = dockerStackConfigTemplate;
const dockerImageRegex = /.+?(:\d+?)?\/.+?:.+$/g;
const dockerVolumesRegex = /:\//g;
Object.assign(dsct.dockerMachineDriverOptions, dsct.dockerMachines.common);
delete dsct.dockerMachines.common;
const manager = dsct.dockerMachines.manager;
manager.engineLabels = manager.engineLabels ||
{
role: 'manager',
affinity: manager.nodeName
};
manager.engineLabels.role = manager.engineLabels.role || 'manager';
manager.engineLabels.affinity = manager.engineLabels.affinity || manager.nodeName;
dsct.dockerMachines.workers = dsct.dockerMachines.workers || [];
dsct.dockerMachines.workers.forEach((worker) => {
worker.engineLabels = worker.engineLabels ||
{
role: 'worker',
affinity: worker.nodeName
};
worker.engineLabels.role = worker.engineLabels.role || 'worker';
worker.engineLabels.affinity = worker.engineLabels.affinity || worker.nodeName;
});
dsct.nfsConfig = dsct.nfsConfig || {
nfsUser: undefined,
nfsPassword: undefined,
nfsSshKeyPath: undefined,
nfsSshPort: 22,
exportBaseDir: undefined,
serverAddr: undefined,
options: undefined
};
dsct.nfsConfig.nfsUser = dsct.nfsConfig.nfsUser || undefined;
dsct.nfsConfig.nfsPassword = dsct.nfsConfig.nfsPassword || undefined;
dsct.nfsConfig.nfsSshKeyPath = dsct.nfsConfig.nfsSshKeyPath || undefined;
dsct.nfsConfig.nfsSshPort = dsct.nfsConfig.nfsSshPort || undefined;
dsct.nfsConfig.exportBaseDir = dsct.nfsConfig.exportBaseDir || undefined;
dsct.nfsConfig.serverAddr = dsct.nfsConfig.serverAddr || undefined;
dsct.nfsConfig.options = dsct.nfsConfig.options || undefined;
dsct.dockerComposeYaml.volumes = dsct.dockerComposeYaml.volumes || {};
for (const volume in dsct.dockerComposeYaml.volumes) {
const v = dsct.dockerComposeYaml.volumes[volume];
if (v.driver_opts && v.driver_opts.type === 'nfs' && v.driver === 'local') {
v.driver_opts.device = v.driver_opts.device || volume;
dockerVolumesRegex.lastIndex = 0;
if (!dockerVolumesRegex.test(v.driver_opts.device)) {
if (!dsct.nfsConfig) {
return cb(new Error(`'${volume}.driver_opts.device' does not begin with ':/' but no 'nfsConfig' is specified`));
}
if (!dsct.nfsConfig.exportBaseDir) {
return cb(new Error(`'${volume}.driver_opts.device' does not begin with ':/' but no 'nfsConfig.exportBaseDir' is specified`));
}
v.driver_opts.device = `:${dsct.nfsConfig.exportBaseDir}/${v.driver_opts.device}`;
}
if (!v.driver_opts.o) {
if (!dsct.nfsConfig) {
return cb(new Error(`'${volume}.driver_opts.o' does not exist but no 'nfsConfig' is specified`));
}
if (!dsct.nfsConfig.serverAddr) {
return cb(new Error(`'${volume}.driver_opts.o' does not exist but no 'nfsConfig.serverAddr' is specified`));
}
if (!dsct.nfsConfig.options) {
return cb(new Error(`'${volume}.driver_opts.o' does not exist but no 'nfsConfig.options' is specified`));
}
v.driver_opts.o = dsct.nfsConfig.options;
}
const optionsHash = DockerProvisionImpl_1.optionsStringToHash(v.driver_opts.o);
if (!optionsHash['addr'] && !dsct.nfsConfig.serverAddr) {
return cb(new Error(`'${volume}.driver_opts.o[addr=]' does not exist but no 'nfsConfig.serverAddr' is specified`));
}
optionsHash['addr'] = optionsHash['addr'] || dsct.nfsConfig.serverAddr;
v.driver_opts.o = DockerProvisionImpl_1.optionsHashToString(optionsHash);
}
}
for (const service in dsct.dockerComposeYaml.services) {
const s = dsct.dockerComposeYaml.services[service];
dockerImageRegex.lastIndex = 0;
if (!dockerImageRegex.test(s.image)) {
if (!dsct.defaultDockerRegistry) {
return cb(new Error(`Service '${service}' missing 'image' property and 'defaultDockerRegistry' is undefined`));
}
if (!dsct.defaultDockerImageTag) {
return cb(new Error(`Service '${service}' missing 'image' property and 'defaultDockerImageTag' is undefined`));
}
if (s.image) {
if (/.+?:\d+?/.test(s.image)) {
s.image = `${dsct.defaultDockerRegistry}/${s.image}`;
}
else {
s.image = `${dsct.defaultDockerRegistry}/${s.image}:${dsct.defaultDockerImageTag}`;
}
}
else {
s.image = `${dsct.defaultDockerRegistry}/${service}:${dsct.defaultDockerImageTag}`;
}
}
const labels = {};
if (s.deploy.labels) {
let traefikPortLabelPresent = 0;
let frontendRuleLabelPresent = 0;
const portRegex = /traefik\.(.*?\.|)port/g;
const frontendRuleRegex = /traefik\.(.*?\.|)frontend.rule/g;
s.deploy.labels.forEach((label) => {
const tuple = label.split('=');
portRegex.lastIndex = frontendRuleRegex.lastIndex = 0;
if (portRegex.test(tuple[0])) {
++traefikPortLabelPresent;
}
if (frontendRuleRegex.test(tuple[0])) {
++frontendRuleLabelPresent;
}
labels[tuple[0]] = tuple[1];
});
if (labels['traefik.enable'] !== 'false') {
if (!traefikPortLabelPresent) {
return cb(new Error(`'traefik.??.port' label not present for service '${service}'`));
}
if (!labels['traefik.backend'] && frontendRuleLabelPresent === 1) {
labels['traefik.backend'] = service;
}
if (!frontendRuleLabelPresent) {
if (!dsct.traefikZoneName) {
return cb(new Error(`'traefik.??.frontend.rule' label and 'traefikZoneName' both undefined for service '${service}'`));
}
labels['traefik.frontend.rule'] = `Host: ${service}.${dsct.traefikZoneName}`;
}
}
s.deploy.labels.length = 0;
for (const label in labels) {
s.deploy.labels.push(`${label}=${labels[label]}`);
}
}
}
this.dockerUtil.writeJsonTemplateFile(dsct, '/tmp/tmp.json');
if (argv.noNfs) {
return cb(null, dsct);
}
me.checkNfsMounts(dsct, (err, dsct) => {
me.commandUtil.processExitIfError(err);
cb(null, dsct);
});
}
checkNfsMounts(dsct, cb) {
const me = this;
const localSpawnOptions = {
suppressStdOut: false,
suppressStdErr: false,
cacheStdOut: true,
cacheStdErr: true,
suppressResult: false
};
const remoteSpawnOptions = Object.assign({}, localSpawnOptions, {
remoteHost: '',
remoteUser: dsct.nfsConfig.nfsUser,
remotePassword: dsct.nfsConfig.nfsPassword
});
const volumes = Object.keys(dsct.dockerComposeYaml.volumes).map((key) => dsct.dockerComposeYaml.volumes[key]);
async.waterfall([
(cb) => {
const nfsServerHash = {};
volumes.forEach((v) => {
if (v.driver_opts && v.driver_opts.type === 'nfs' && v.driver === 'local') {
const host = DockerProvisionImpl_1.optionsStringToHash(v.driver_opts.o).addr;
nfsServerHash[host] = nfsServerHash[host] || [];
nfsServerHash[host].push(v.driver_opts.device.slice(1));
}
});
const nfsServers = Object.keys(nfsServerHash).map((key) => key);
async.each(nfsServers, (nfsServer, cb) => {
me.spawn.spawnShellCommandAsync([
'showmount',
'-e',
nfsServer
], localSpawnOptions, () => {
}, (err, result) => {
if (err) {
const resultObject = me.safeJson.safeParseSync(err.message);
if (resultObject.obj.code !== 0) {
const cmds = [
[
'apt-get',
'update'
],
[
'apt-get',
'install',
'-y',
'nfs-kernel-server'
]
];
return me.remoteSpawnCmdArray(cmds, Object.assign({}, remoteSpawnOptions, { remoteHost: nfsServer }), (err) => {
me.callbackAndExitIfError(err, cb);
cb();
});
}
}
cb();
});
}, (err) => {
me.callbackAndExitIfError(err, cb);
cb(null, nfsServerHash);
});
},
(nfsServerHash, cb) => {
const nfsServers = Object.keys(nfsServerHash).map((key) => key);
async.each(nfsServers, (nfsServer, cb) => {
me.spawn.spawnShellCommandAsync([
'showmount',
'-e',
nfsServer
], localSpawnOptions, () => {
}, (err, result) => {
me.safeJson.safeParse(result, (err, obj) => {
if (obj.code) {
return cb(new Error(`local 'showmount' FAILED`));
}
const exportList = obj.stdoutText.split('\n').slice(1, -1).map((exportLine) => exportLine.split(/\s/)[0]);
const volumes = nfsServerHash[nfsServer];
const addExportSpawnOptions = Object.assign({}, remoteSpawnOptions, { remoteHost: nfsServer });
async.series([
(cb) => {
async.each(volumes, (volume, cb) => {
const msg = `Checking NFS mount ${nfsServer}:${volume} ...`;
if (exportList.indexOf(volume) !== -1) {
me.commandUtil.log(`${msg} OK`);
return cb();
}
me.commandUtil.log(`${msg} NOT EXPORTED`);
me.commandUtil.log(`Attempting to export NFS volume ${nfsServer}:${volume} ...`);
if (!addExportSpawnOptions.remoteHost || !addExportSpawnOptions.remoteUser || !addExportSpawnOptions.remotePassword) {
return cb(new Error(`nfsConfig [nfsHost && (nfsUser + nfsPassword) || nfsSshKeyPath] must be specified to export NFS volume. Cannot continue.`));
}
const etcExportsEntry = `${volume} *(insecure,rw,sync,no_root_squash,no_subtree_check)`;
const cmds = [
[
'mkdir',
'-p',
volume
],
[
'chmod',
'777',
volume
],
[
`grep -q -F '${etcExportsEntry}' /etc/exports || echo '${etcExportsEntry}' >> /etc/exports`
]
];
me.remoteSpawnCmdArray(cmds, addExportSpawnOptions, cb);
}, cb);
},
(cb) => {
const cmds = [
[
'/usr/sbin/exportfs',
'-ra'
]
];
me.remoteSpawnCmdArray(cmds, addExportSpawnOptions, (err) => {
me.commandUtil.log(`Restarted NFS services on ${nfsServer}`);
cb(err);
});
}
], cb);
});
});
}, cb);
}
], (err) => {
cb(err, dsct);
});
}
remoteSpawnCmdArray(cmds, remoteSpawnOptions, cb) {
const me = this;
async.eachSeries(cmds, (cmd, cb) => {
me.spawn.spawnShellCommandAsync(cmd, remoteSpawnOptions, (err, result) => {
me.commandUtil.log(result);
}, (err, result) => {
me.commandUtil.log(result);
cb(err);
});
}, (err) => {
cb(err);
});
}
static optionsHashToString(hash) {
let retVal = '';
for (const key in hash) {
retVal += key + ((hash[key]) ? `=${hash[key]},` : ',');
}
return retVal.slice(0, -1);
}
static optionsStringToHash(optionsString) {
const hash = {};
optionsString.split(',').forEach((option) => {
const optionWithValue = option.split('=');
hash[optionWithValue[0]] = optionWithValue[1];
});
return hash;
}
extractYamlFromJson(argv, cb = null) {
const me = this;
const { fullInputPath, stackConfigTemplate } = me.getContainerConfigsFromJsonFile(argv.inputJsonFile);
me.createOutputPath(fullInputPath, argv.outputYamlFile, '.yaml', (err, outputYamlPath, outputFileExists) => {
me.callbackAndExitIfError(err, cb);
if (outputFileExists && !me.positive.areYouSure(`Output file '${outputYamlPath}' already exists. Overwrite? [Y/n] `, 'Operation canceled.', true, firmament_yargs_1.FailureRetVal.TRUE)) {
me.callbackAndExitWithError(err, cb);
}
const yaml = YAML.stringify(stackConfigTemplate.dockerComposeYaml, 8, 2);
fs.writeFile(outputYamlPath, yaml, (err) => {
me.callbackAndExitWithError(err, cb);
});
});
}
makeTemplate(argv, cb = null) {
const me = this;
me.composeAndWriteTemplate(argv.get, argv.dm, argv.yaml, argv.output, (err, msg) => {
me.callbackAndExitWithError(err, cb);
});
}
buildTemplate(argv, cb = null) {
const me = this;
const { fullInputPath, stackConfigTemplate } = me.getContainerConfigsFromJsonFile(argv.input);
me.validateDockerStackConfigTemplate(argv, stackConfigTemplate, (err, stackConfigTemplate) => {
if (err) {
if (cb) {
return cb(err);
}
me.commandUtil.processExitWithError(err, 'OK');
}
me.stackConfigTemplate = stackConfigTemplate;
switch (stackConfigTemplate.dockerMachineDriverOptions.driver) {
case 'openstack': {
const dmdo = stackConfigTemplate.dockerMachineDriverOptions;
if (argv.username) {
dmdo.openstackUsername = argv.username;
}
if (argv.password) {
dmdo.openstackPassword = argv.password;
}
break;
}
case 'vmwarevsphere': {
const dmdo = stackConfigTemplate.dockerMachineDriverOptions;
if (argv.username) {
dmdo.vmwarevsphereUsername = argv.username;
}
if (argv.password) {
dmdo.vmwarevspherePassword = argv.password;
}
break;
}
case 'amazonec2':
case 'virtualbox':
default:
break;
}
me.commandUtil.log("Constructing Docker Stack described in: '" + fullInputPath + "'");
me.createDockerMachines(fullInputPath, stackConfigTemplate, argv, (err, result) => {
if (cb) {
return cb();
}
me.commandUtil.processExitWithError(err, 'OK');
});
});
}
convertOptionsFromCamelToSnakeCase(options) {
const me = this;
const retVal = [];
const excludeProperties = ['nodeCount', 'nodeName'];
for (const option in options) {
if (excludeProperties.indexOf(option) !== -1) {
continue;
}
if (option === 'engineLabels') {
for (const engineLabel in options[option]) {
retVal.push('--engine-label');
retVal.push(`${engineLabel}=${options[option][engineLabel]}`);
}
continue;
}
const optionKey = me.camelToSnake(option, '-');
const optionValue = options[option];
const keyValueSeparator = ((typeof optionValue !== 'string') || (optionValue.indexOf('=') === -1)) ? '=' : ' ';
retVal.push(`--${optionKey}${keyValueSeparator}${optionValue}`);
}
return retVal;
}
logErrAndResult(err, result) {
const me = this;
if (err) {
return me.commandUtil.log(err.message);
}
me.commandUtil.log((result || '').toString());
}
createDockerMachines(fullInputPath, stackConfigTemplate, argv, cb) {
const me = this;
cb = me.checkCallback(cb);
const createOptions = me.convertOptionsFromCamelToSnakeCase(stackConfigTemplate.dockerMachineDriverOptions);
async.waterfall([
(cb) => {
const managerMachineName = `${stackConfigTemplate.stackName}-${stackConfigTemplate.dockerMachines.manager.nodeName}`;
const managerDockerMachineCmd = createOptions.slice();
managerDockerMachineCmd.push.apply(managerDockerMachineCmd, me.convertOptionsFromCamelToSnakeCase(stackConfigTemplate.dockerMachines.manager));
managerDockerMachineCmd.push(managerMachineName);
me.createDockerMachine(managerDockerMachineCmd, cb);
},
(managerMachineName, ip, cb) => {
const dockerMachineInitSwarmCmd = [
'docker-machine',
'ssh',
managerMachineName,
'docker',
'swarm',
'init',
'--advertise-addr',
ip
];
if (me.writeScripts) {
fs.writeFileSync(`/home/jreeme/tmp/_init_swarm.sh`, dockerMachineInitSwarmCmd.join(' '));
return cb(null, managerMachineName, ip);
}
me.spawn.spawnShellCommandAsync(dockerMachineInitSwarmCmd, {
cacheStdOut: true
}, me.logErrAndResult.bind(me), (_err) => {
if (_err) {
const { err, obj } = me.safeJson.safeParseSync(_err.message);
if (!err && obj.code === 1) {
return cb(null, managerMachineName, ip);
}
}
cb(_err, managerMachineName, ip);
});
},
(managerMachineName, ip, cb) => {
const dockerMachineInitSwarmCmd = [
'docker-machine',
'ssh',
managerMachineName,
'docker',
'swarm',
'join-token',
'worker',
'-q'
];
if (me.writeScripts) {
fs.writeFileSync(`/home/jreeme/tmp/_join_swarm.sh`, dockerMachineInitSwarmCmd.join(' '));
return cb(null, managerMachineName, ip, 'joinToken');
}
me.spawn.spawnShellCommandAsync(dockerMachineInitSwarmCmd, {
cacheStdOut: true
}, me.logErrAndResult.bind(me), (err, result) => {
const joinToken = me.safeJson.safeParseSync(result).obj.stdoutText.trim();
cb(null, managerMachineName, ip, joinToken);
});
},
(managerMachineName, managerIp, joinToken, cb) => {
let fnArray = [];
stackConfigTemplate.dockerMachines.workers.forEach((workerDockerMachine) => {
for (let i = 0; i < workerDockerMachine.nodeCount; ++i) {
const workerMachineName = `${stackConfigTemplate.stackName}-${workerDockerMachine.nodeName}-${i}`;
const workerDockerMachineCmd = createOptions.slice();
workerDockerMachineCmd.push.apply(workerDockerMachineCmd, me.convertOptionsFromCamelToSnakeCase(workerDockerMachine));
workerDockerMachineCmd.push(workerMachineName);
fnArray.push(async.apply(me.createWorkerDockerMachine.bind(me), workerDockerMachineCmd, managerIp, joinToken));
}
});
async.parallel(fnArray, (err) => {
cb(err, managerMachineName, managerIp);
});
},
(managerMachineName, ip, cb) => {
const dockerMachineGetMasterEnvCmd = [
'docker-machine',
'env',
managerMachineName
];
me.spawn.spawnShellCommandAsync(dockerMachineGetMasterEnvCmd, {
cacheStdOut: true
}, (err, result) => {
me.commandUtil.log(result.toString());
}, (err, result) => {
const regex = /export (.*)/g;
const envString = me.safeJson.safeParseSync(result).obj.stdoutText.trim();
let match;
let env = {};
do {
match = regex.exec(envString);
if (match) {
const keyValue = match[1].split('=');
env[keyValue[0]] = keyValue[1].replace(/"/g, '');
}
} while (match);
cb(null, env, ip);
});
},
(env, ip, cb) => {
tmp.file({ dir: path.dirname(fullInputPath) }, (err, tmpPath, fd, cleanupCb) => {
try {
if (argv.noPorts) {
for (const serviceName in stackConfigTemplate.dockerComposeYaml.services) {
const service = stackConfigTemplate.dockerComposeYaml.services[serviceName];
service.ports && delete service.ports;
}
}
}
catch (err) {
me.commandUtil.error(`Failed to execute 'noPorts' option ${err}`);
}
const yaml = YAML.stringify(stackConfigTemplate.dockerComposeYaml, 8, 2).replace(/\$\{MASTER_IP\}/g, ip);
if (me.commandUtil.callbackIfError(err)) {
return;
}
fs.writeFile(tmpPath, yaml, (err) => {
if (err) {
me.logErrAndResult(err, '');
return cb(err);
}
const dockerMachineDeployCmd = [
'docker',
'stack',
'deploy',
'-c',
tmpPath,
stackConfigTemplate.stackName ? stackConfigTemplate.stackName : stackConfigTemplate.clusterPrefix
];
me.spawn.spawnShellCommandAsync(dockerMachineDeployCmd, {
env,
cacheStdOut: true
}, (err, result) => {
me.commandUtil.log(result.toString());
}, (err, result) => {
me.logErrAndResult(err, result);
cleanupCb();
cb();
});
});
});
}
], (err, result) => {
me.logErrAndResult(err, result);
cb(err, result);
});
}
createWorkerDockerMachine(dockerMachineCmd, managerIp, joinToken, cb) {
const me = this;
me.createDockerMachine(dockerMachineCmd, (err, workerMachineName, workerIp) => {
if (err) {
return cb(err);
}
const dockerMachineJoinSwarmCmd = [
'docker-machine',
'ssh',
workerMachineName,
'docker',
'swarm',
'join',
'--advertise-addr',
`${workerIp}:2377`,
'--token',
joinToken,
`${managerIp}:2377`
];
if (me.writeScripts) {
fs.writeFileSync(`/home/jreeme/tmp/_join_worker_${workerMachineName}.sh`, dockerMachineJoinSwarmCmd.join(' '));
return cb(null, '');
}
me.spawn.spawnShellCommandAsync(dockerMachineJoinSwarmCmd, {
cacheStdOut: true,
cacheStdErr: true
}, (err, result) => {
me.commandUtil.log(result.toString());
}, (err, result) => {
cb(null, result);
});
});
}
handleDockerMachineExecutionFailure(err, cb) {
const me = this;
me.safeJson.safeParse(err.message, (err, obj) => {
try {
if (obj.code.code === 'ENOENT') {
if (me.positive.areYouSure(`Looks like 'docker-machine' is not installed. Want me to try to install it?`, 'Operation canceled.', true, firmament_yargs_1.FailureRetVal.TRUE)) {
const installDockerMachineJson = path.resolve(__dirname, '../../firmament-bash/install-docker-machine.json');
return me.processCommandJson.processAbsoluteUrl(installDockerMachineJson, (err) => {
cb(new Error(`'docker-machine' installed. Try provisioning again.`));
});
}
return cb(new Error('docker-machine installation canceled'));
}
cb(null);
}
catch (err) {
cb(err);
}
});
}
createDockerMachine(dockerMachineCreateCmdOptions, cb) {
const me = this;
async.waterfall([
(cb) => {
const dockerMachineCmd = [
'docker-machine',
'create'
].concat(dockerMachineCreateCmdOptions);
if (me.writeScripts) {
fs.writeFileSync(`/home/jreeme/tmp/_create_${dockerMachineCmd[dockerMachineCmd.length - 1]}.sh`, dockerMachineCmd.join(' '));
return cb(null, '');
}
me.spawn.spawnShellCommandAsync(dockerMachineCmd, {}, (err, result) => {
me.commandUtil.log(result.toString());
}, (err) => {
const machineName = dockerMachineCmd[dockerMachineCmd.length - 1];
if (err) {
return me.handleDockerMachineExecutionFailure(err, (err) => {
me.commandUtil.processExitIfError(err);
cb(err, machineName);
});
}
switch (me.stackConfigTemplate.dockerMachineDriverOptions.driver) {
case ('vmwarevsphere'):
return me.finalConfig_VMWareVSphere(machineName, cb);
case ('openstack'):
return me.finalConfig_OpenStack(machineName, cb);
case ('amazonec2'):
return me.finalConfig_AmazonEC2(machineName, cb);
case ('virtualbox'):
default:
return me.finalConfig_VirtualBox(machineName, cb);
}
});
},
(machineName, cb) => {
const dockerMachineGetIpCmd = [
'docker-machine',
'ip',
machineName
];
if (me.writeScripts) {
fs.writeFileSync(`/home/jreeme/tmp/_get_managerIp.sh`, dockerMachineGetIpCmd.join(' '));
return cb(null, machineName, '0.0.0.0');
}
me.spawn.spawnShellCommandAsync(dockerMachineGetIpCmd, {
cacheStdOut: true
}, me.logErrAndResult.bind(me), (err, result) => {
const ip = me.safeJson.safeParseSync(result).obj.stdoutText.trim();
cb(err, { machineName, ip });
});
}
], (err, result) => {
const { machineName, ip } = result;
cb(err, machineName, ip);
});
}
;
finalConfig_VirtualBox(machineName, cb) {
this.adjustBoot2DockerProfile(machineName, cb);
}
finalConfig_AmazonEC2(machineName, cb) {
cb(null, machineName);
}
finalConfig_OpenStack(machineName, cb) {
cb(null, machineName);
}
finalConfig_VMWareVSphere(machineName, cb) {
this.adjustBoot2DockerProfile(machineName, cb);
}
adjustBoot2DockerProfile(machineName, cb) {
const me = this;
const profileLines = (me.stackConfigTemplate.hostMachineDnsServer)
? `
#Need to set vm.max_map_count to 262144 to support ElasticSearch (ES fails in production without it)
sysctl -w vm.max_map_count=262144
sysctl -w net.ipv4.tcp_keepalive_time=600
ulimit -l unlimited
#swapoff /dev/sda2
#sed -i '\\''/sda2/ s/^/#/'\\'' /etc/fstab
NETDEVICES="$(awk -F: '\\''/eth.:|tr.:/{print $1}'\\'' /proc/net/dev 2>/dev/null)"
for DEVICE in $NETDEVICES; do
DHCP_PID_FILE=/var/run/udhcpc.$DEVICE.pid
echo "Checking existence of $DHCP_PID_FILE ..."
until [ -f $DHCP_PID_FILE ];
do
echo "Waiting for $DHCP_PID_FILE to exist ..."
sleep 1
done
done
echo "$DHCP_PID_FILE exists ..."
RESOLV_CONF=/etc/resolv.conf
echo "Checking existence of $RESOLV_CONF ..."
until [ -f $RESOLV_CONF ];
do
echo "Waiting for $RESOLV_CONF to exist ..."
sleep 1
done
echo "$RESOLV_CONF exists ..."
DATE_OF_DHCP_PID_FILE=$(date -u -r $DHCP_PID_FILE +%s)
DATE_OF_RESOLV_CONF=$(date -u -r $RESOLV_CONF +%s)
NOW=$(date -u +%s)
echo "$DHCP_PID_FILE last modified at $DATE_OF_DHCP_PID_FILE"
echo "$RESOLV_CONF last modified at $DATE_OF_RESOLV_CONF"
echo $(( \${DATE_OF_DHCP_PID_FILE}-\${DATE_OF_RESOLV_CONF} ))
echo 'nameserver ${me.stackConfigTemplate.hostMachineDnsServer}' > /etc/resolv.conf
#Try to protect against resolver restart or DHCP lease renewal rewriting our resolv.conf file
chmod 444 /etc/resolv.conf
` : `
#Need to set vm.max_map_count to 262144 to support ElasticSearch (ES fails in production without it)
sysctl -w vm.max_map_count=262144
sysctl -w net.ipv4.tcp_keepalive_time=600
ulimit -l unlimited
#swapoff /dev/sda2
#sed -i '\\''/sda2/ s/^/#/'\\'' /etc/fstab
`;
const dockerMachineJoinSwarmCmd = [
`echo '${profileLines}' | sudo tee -a /var/lib/boot2docker/profile && sudo /etc/init.d/docker restart`
];
me.runCommandOnDockerMachineHost(machineName, dockerMachineJoinSwarmCmd, (err, result) => {
err && me.commandUtil.log(err.toString());
cb(err, machineName);
});
}
runCommandOnDockerMachineHost(machineName, commandArray, cb) {
const me = this;
commandArray.unshift('docker-machine', 'ssh', machineName);
me.spawn.spawnShellCommandAsync(commandArray, {
cacheStdOut: true
}, (err, result) => {
me.commandUtil.log(result.toString());
}, cb);
}
getContainerConfigsFromJsonFile(inputPath) {
const me = this;
const fullInputPath = me.commandUtil.getConfigFilePath(inputPath, '.json');
if (!fileExists.sync(fullInputPath)) {
me.commandUtil.processExitWithError(new Error(`\n'${fullInputPath}' does not exist`));
}
const stackConfigTemplate = me.safeJson.readFileSync(fullInputPath, undefined);
return { fullInputPath, stackConfigTemplate };
}
composeAndWriteTemplate(catalogEntryName, dockerMachineHostType, dockerComposeYamlPath, outputTemplateFileName, cb) {
const me = this;
me.createOutputPath(path.resolve(process.cwd(), 'tmp.txt'), outputTemplateFileName, '.json', (err, fullOutputPath, outputFileExists) => {
if (catalogEntryName === undefined) {
if (fileExists.sync(fullOutputPath)
&& !me.positive.areYouSure(`Config file '${fullOutputPath}' already exists. Overwrite? [Y/n] `, 'Operation canceled.', true, firmament_yargs_1.FailureRetVal.TRUE)) {
return cb(null);
}
const dockerMachineWrapperPath = path.resolve(__dirname, `../../docker/docker-machine-wrappers/${dockerMachineHostType}.json`);
const dockerMachineWrapper = me.safeJson.readFileSync(dockerMachineWrapperPath, undefined);
const dockerComposeYaml = YAML.load(dockerComposeYamlPath);
const jsonTemplate = Object.assign({}, dockerMachineWrapper, { dockerComposeYaml });
me.dockerUtil.writeJsonTemplateFile(jsonTemplate, fullOutputPath);
cb(null, 'Template written.');
}
else {
cb(new Error('Provision from template not implemented.'));
}
});
}
callbackAndExitIfError(err, cb) {
if (!err) {
return;
}
if (cb) {
cb();
}
this.commandUtil.processExitWithError(err, 'OK');
}
callbackAndExitWithError(err, cb) {
if (cb) {
cb();
}
this.commandUtil.processExitWithError(err, 'OK');
}
camelToSnake(name, separator) {
return name.replace(/([a-z]|(?:[A-Z]+))([A-Z]|$)/g, function (_, $1, $2) {
return $1 + ($2 && (separator || '_') + $2);
}).toLowerCase();
}
createOutputPath(inPathFragment, outPathFragment, extension, cb) {
if (path.extname(outPathFragment) !== extension) {
outPathFragment += extension;
}
if (!path.isAbsolute(outPathFragment)) {
outPathFragment = path.resolve(path.dirname(inPathFragment), outPathFragment);
}
const pathDirname = path.dirname(outPathFragment);
mkdirp(pathDirname, (err) => {
if (err) {
return cb(err, outPathFragment);
}
fileExists(outPathFragment, (err, exists) => {
touch(outPathFragment, (err) => {
if (err || exists) {
return cb(err, outPathFragment, exists);
}
fs.unlink(outPathFragment, (err) => {
return cb(err, outPathFragment, exists);
});
});
});
});
}
;
};
DockerProvisionImpl = DockerProvisionImpl_1 = __decorate([
inversify_1.injectable(),
__param(0, inversify_1.inject('CommandUtil')),
__param(1, inversify_1.inject('Spawn')),
__param(2, inversify_1.inject('SafeJson')),
__param(3, inversify_1.inject('RemoteCatalogGetter')),
__param(4, inversify_1.inject('ProcessCommandJson')),
__param(5, inversify_1.inject('DockerUtil')),
__param(6, inversify_1.inject('DockerImageManagement')),
__param(7, inversify_1.inject('DockerContainerManagement')),
__param(8, inversify_1.inject('Positive')),
__param(9, inversify_1.inject('ProgressBar')),
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object, Object, Object, Object])
], DockerProvisionImpl);
exports.DockerProvisionImpl = DockerProvisionImpl;
//# sourceMappingURL=docker-provision-impl.js.map