list ldap as 0.0.25 change
This commit is contained in:
Executable
+340
@@ -0,0 +1,340 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert'),
|
||||
async = require('async'),
|
||||
crypto = require('crypto'),
|
||||
execSync = require('child_process').execSync,
|
||||
fs = require('fs'),
|
||||
https = require('https'),
|
||||
os = require('os'),
|
||||
path = require('path'),
|
||||
program = require('commander'),
|
||||
readlineSync = require('readline-sync'),
|
||||
spawn = require('child_process').spawn,
|
||||
SshClient = require('ssh2').Client,
|
||||
superagent = require('superagent'),
|
||||
util = require('util');
|
||||
|
||||
require('colors');
|
||||
|
||||
var SSH = 'root@%s -tt -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=10 -i %s';
|
||||
var sshKeyPath = path.join(process.env.HOME, '/.ssh/id_rsa_yellowtent');
|
||||
|
||||
if (!process.env['DIGITAL_OCEAN_TOKEN_DEV']) exit('Missing env variable DIGITAL_OCEAN_TOKEN_DEV');
|
||||
if (!process.env['DIGITAL_OCEAN_TOKEN_STAGING']) exit('Missing env variable DIGITAL_OCEAN_TOKEN_STAGING');
|
||||
|
||||
if (!fs.existsSync(sshKeyPath)) exit('Unable to find ssh key path. Searching for ' + sshKeyPath);
|
||||
|
||||
// Allow self signed certs!
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
|
||||
function exit(error) {
|
||||
if (error) console.log(error);
|
||||
process.exit(error ? 1 : 0);
|
||||
}
|
||||
|
||||
function getDroplets(token, callback) {
|
||||
assert.strictEqual(typeof token, 'string');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
var droplets = [];
|
||||
var nextPage = null;
|
||||
|
||||
async.doWhilst(function (callback) {
|
||||
var url = nextPage ? nextPage : 'https://api.digitalocean.com/v2/droplets';
|
||||
|
||||
superagent.get(url).set('Authorization', 'Bearer ' + token).end(function (error, result) {
|
||||
if (error) return callback(error.message);
|
||||
if (result.statusCode === 403) return callback('Invalid Digitalocean credentials');
|
||||
if (result.statusCode !== 200) return callback(util.format('Unable to get droplet list. %s - %s', result.statusCode, result.text));
|
||||
|
||||
nextPage = (result.body.links && result.body.links.pages) ? result.body.links.pages.next : null;
|
||||
droplets = droplets.concat(result.body.droplets);
|
||||
|
||||
callback(null);
|
||||
});
|
||||
}, function () { return !!nextPage; }, function (error) {
|
||||
if (error) return callback(error);
|
||||
callback(null, droplets);
|
||||
});
|
||||
}
|
||||
|
||||
function selectCloudron(action) {
|
||||
assert.strictEqual(typeof action, 'function');
|
||||
|
||||
var dropletsDev = [];
|
||||
var dropletsStaging = [];
|
||||
var dropletsProd = [];
|
||||
|
||||
console.log('Getting droplet lists from dev and staging...');
|
||||
|
||||
getDroplets(process.env['DIGITAL_OCEAN_TOKEN_DEV'], function (error, result) {
|
||||
if (error) exit(error);
|
||||
|
||||
dropletsDev = result;
|
||||
|
||||
getDroplets(process.env['DIGITAL_OCEAN_TOKEN_STAGING'], function (error, result) {
|
||||
if (error) exit(error);
|
||||
|
||||
dropletsStaging = result;
|
||||
|
||||
getDroplets(process.env['DIGITAL_OCEAN_TOKEN_PROD'], function (error, result) {
|
||||
if (error) exit(error);
|
||||
|
||||
dropletsProd = result;
|
||||
|
||||
console.log();
|
||||
console.log('Available Droplets on dev:'.bold);
|
||||
dropletsDev.forEach(function (droplet, index) {
|
||||
console.log('\t(%s)\t%s %s', index, droplet.name.cyan, droplet.networks.v4[0].ip_address);
|
||||
});
|
||||
|
||||
console.log();
|
||||
console.log('Available Droplets on staging:'.bold);
|
||||
dropletsStaging.forEach(function (droplet, index) {
|
||||
console.log('\t(%s)\t%s %s', dropletsDev.length + index, droplet.name.cyan, droplet.networks.v4[0].ip_address);
|
||||
});
|
||||
|
||||
console.log();
|
||||
console.log('Available Droplets on prod:'.bold);
|
||||
dropletsProd.forEach(function (droplet, index) {
|
||||
console.log('\t(%s)\t%s %s', dropletsDev.length + dropletsStaging.length + index, droplet.name.cyan, droplet.networks.v4[0].ip_address);
|
||||
});
|
||||
|
||||
console.log();
|
||||
|
||||
var droplets = dropletsDev.concat(dropletsStaging).concat(dropletsProd);
|
||||
|
||||
var index = -1;
|
||||
while (true) {
|
||||
index = parseInt(readlineSync.question('Choose cloudron [0-' + (droplets.length-1) + ']: ', {}));
|
||||
if (isNaN(index) || index < 0 || index > droplets.length-1) console.log('Invalid selection'.red);
|
||||
else break;
|
||||
}
|
||||
|
||||
action(droplets[index].networks.v4[0].ip_address);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loginToCloudron(ip) {
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
|
||||
console.log('Ssh into %s'.bold, ip.cyan);
|
||||
|
||||
var ssh = spawn('ssh', util.format(SSH, ip, sshKeyPath).split(' '));
|
||||
ssh.on('exit', exit);
|
||||
ssh.on('error', exit);
|
||||
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.setRawMode(true);
|
||||
|
||||
process.stdin.pipe(ssh.stdin);
|
||||
ssh.stdout.pipe(process.stdout);
|
||||
ssh.stderr.pipe(process.stderr);
|
||||
|
||||
process.stdin.resume();
|
||||
|
||||
}
|
||||
|
||||
function logsFromCloudron(ip, fileName, tail) {
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
assert.strictEqual(typeof fileName, 'string');
|
||||
assert.strictEqual(typeof tail, 'boolean');
|
||||
|
||||
console.log('Fetching logs from'.bold, ip.cyan);
|
||||
|
||||
var options = {
|
||||
hostname: ip,
|
||||
port: 886,
|
||||
path: util.format('/api/v1/installer/logs?filename=%s&tail=%s', fileName, tail),
|
||||
method: 'GET',
|
||||
key: fs.readFileSync(path.join(__dirname, '../../keys/installer/server.key')),
|
||||
cert: fs.readFileSync(path.join(__dirname, '../../keys/installer/server.crt')),
|
||||
ca: fs.readFileSync(path.join(__dirname, '../../keys/installer_ca/ca.crt')),
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
var req = https.request(options, function (res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
process.stdout.write(chunk);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function (error) {
|
||||
exit(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
}
|
||||
|
||||
function triggerBackup(ip) {
|
||||
assert.strictEqual(typeof ip, 'string');
|
||||
|
||||
console.log('Trigger backup on %s'.bold, ip.cyan);
|
||||
|
||||
var options = {
|
||||
hostname: ip,
|
||||
port: 886,
|
||||
path: '/api/v1/installer/backup',
|
||||
method: 'POST',
|
||||
key: fs.readFileSync(path.join(__dirname, '../../keys/installer/server.key')),
|
||||
cert: fs.readFileSync(path.join(__dirname, '../../keys/installer/server.crt')),
|
||||
ca: fs.readFileSync(path.join(__dirname, '../../keys/installer_ca/ca.crt')),
|
||||
rejectUnauthorized: false
|
||||
};
|
||||
|
||||
var req = https.request(options, function (res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
process.stdout.write(chunk);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function (error) {
|
||||
exit(error);
|
||||
});
|
||||
|
||||
req.end();
|
||||
}
|
||||
|
||||
function sshExec(ip, cmds) {
|
||||
var privateKey = path.join(process.env.HOME, '.ssh/id_rsa_yellowtent');
|
||||
if (!fs.existsSync(privateKey)) exit('cannot find private key');
|
||||
|
||||
var sshClient = new SshClient();
|
||||
sshClient.connect({
|
||||
host: ip,
|
||||
port: 22,
|
||||
username: 'root',
|
||||
privateKey: fs.readFileSync(privateKey)
|
||||
});
|
||||
sshClient.on('ready', function () {
|
||||
console.log('connected');
|
||||
|
||||
async.eachSeries(cmds, function (cmd, iteratorDone) {
|
||||
console.log(cmd.cmd.yellow);
|
||||
|
||||
sshClient.exec(cmd.cmd, function(err, stream) {
|
||||
if (err) exit(err.message);
|
||||
|
||||
if (cmd.stdin) cmd.stdin.pipe(stream);
|
||||
stream.pipe(process.stdout);
|
||||
stream.on('close', function () {
|
||||
iteratorDone();
|
||||
});
|
||||
});
|
||||
}, function seriesDone(error) {
|
||||
if (error) exit(error.message);
|
||||
|
||||
console.log('Done patching'.green);
|
||||
sshClient.end();
|
||||
});
|
||||
});
|
||||
sshClient.on('error', function (error) {
|
||||
exit(error.message);
|
||||
});
|
||||
sshClient.on('exit', function (exitCode) {
|
||||
console.log('exit');
|
||||
process.exit(exitCode);
|
||||
});
|
||||
}
|
||||
|
||||
function hotfixCloudron(ip, code) {
|
||||
var CMDS = [
|
||||
{ cmd: 'supervisorctl stop all' },
|
||||
{ cmd: 'rm -rf /home/yellowtent/box/* /home/yellowtent/box/.*' },
|
||||
{ cmd: 'tar zxf - -C /home/yellowtent/box', stdin: fs.createReadStream(code) },
|
||||
{ cmd: 'cd /home/yellowtent/box && npm rebuild' },
|
||||
{ cmd: 'chown -R yellowtent.yellowtent /home/yellowtent/box' },
|
||||
{ cmd: 'sed -e "s/restoreUrl/_restoreUrl/" -i /home/yellowtent/setup_start.sh' }, // do not restore
|
||||
{ cmd: '/home/yellowtent/setup_start.sh' } // ensure db-migrate runs as well
|
||||
];
|
||||
|
||||
sshExec(ip, CMDS);
|
||||
}
|
||||
|
||||
function hotfix(options) {
|
||||
var code;
|
||||
|
||||
if (!options.code) {
|
||||
var answer = readlineSync.question('Create a tarball from repo (y/n)? ');
|
||||
if (answer !== 'y') return exit();
|
||||
code = os.tmpdir() + '/boxtarball.tar.gz';
|
||||
execSync(path.join(__dirname, '../images/createBoxTarball --output ' + code + ' --no-upload'), { stdio: [ null, process.stdout, process.stderr ] });
|
||||
} else {
|
||||
code = options.code;
|
||||
}
|
||||
|
||||
if (!options.ip) {
|
||||
selectCloudron(function (ip) { hotfixCloudron(ip, code); });
|
||||
} else {
|
||||
hotfixCloudron(options.ip, code);
|
||||
}
|
||||
}
|
||||
|
||||
function login(options) {
|
||||
if (!options.ip) selectCloudron(loginToCloudron);
|
||||
else loginToCloudron(options.ip);
|
||||
}
|
||||
|
||||
function logs(options) {
|
||||
var fileName = '/var/log/supervisor/box.log';
|
||||
|
||||
if (options.installer) fileName = '/var/log/cloudron/installserver.log';
|
||||
if (options.nginxAccess) fileName = '/var/log/nginx/access.log';
|
||||
if (options.nginxError) fileName = '/var/log/nginx/error.log';
|
||||
|
||||
if (!options.ip) selectCloudron(function (ip) { logsFromCloudron(ip, fileName, !!options.tail); });
|
||||
else logsFromCloudron(options.ip, fileName, !!options.tail);
|
||||
}
|
||||
|
||||
function backup(options) {
|
||||
if (!options.ip) selectCloudron(triggerBackup);
|
||||
else triggerBackup(options.ip);
|
||||
}
|
||||
|
||||
// entry point
|
||||
program.version('0.1.0');
|
||||
|
||||
program.command('login')
|
||||
.description('Login to cloudron')
|
||||
.option('--ip <value>', 'Cloudron IP')
|
||||
.action(login);
|
||||
|
||||
program.command('logs')
|
||||
.description('Fetch logs by filename')
|
||||
.option('--ip <value>', 'Cloudron IP')
|
||||
.option('-f, --tail', 'tail the logs')
|
||||
.option('--installer', 'installer logs')
|
||||
.option('--nginx-error', 'nginx error logs')
|
||||
.option('--nginx-access', 'nginx access logs')
|
||||
.option('--box', 'box logs [default]')
|
||||
.action(logs);
|
||||
|
||||
program.command('hotfix')
|
||||
.description('Hotfix a cloudron')
|
||||
.option('--ip <value>', 'Cloudron IP')
|
||||
.option('--code <code>', 'Code tarball')
|
||||
.action(hotfix);
|
||||
|
||||
program.command('backup')
|
||||
.description('Backup a cloudron')
|
||||
.option('--ip <value>', 'Cloudron IP')
|
||||
.action(backup);
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.outputHelp();
|
||||
} else { // https://github.com/tj/commander.js/issues/338
|
||||
var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2]; });
|
||||
if (!knownCommand) {
|
||||
console.error('Unknown command: ' + process.argv[2]);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user