heroku-apps
Version:
Heroku CLI plugin to manage apps.
126 lines (112 loc) • 3.95 kB
JavaScript
;
let cli = require('heroku-cli-util');
let co = require('co');
let _ = require('lodash');
let strftime = require('strftime');
let trunc = s => _.trunc(s, {length: 35, omission: '…'});
function printJSON (data) {
cli.log(JSON.stringify(data.dynos, null, 2));
}
function timeAgo (since) {
let elapsed = Math.floor((new Date() - since)/1000);
let message = strftime('%Y/%m/%d %H:%M:%S', since);
if (elapsed <= 60) return `${message} (~ ${Math.floor(elapsed)}s ago)`;
else if (elapsed <= 60*60) return `${message} (~ ${Math.floor(elapsed/60)}m ago)`;
else if (elapsed <= 60*60*25) return `${message} (~ ${Math.floor(elapsed/60/60)}h ago)`;
else return message;
}
function timeRemaining (from, to) {
let secs = Math.floor(to/1000 - from/1000);
let mins = Math.floor(secs / 60);
let hours = Math.floor(mins / 60);
if (hours > 0) return `${hours}h ${mins % 60}m`;
if (mins > 0) return `${mins}m ${secs % 60}s`;
if (secs > 0) return `${secs}s`;
return '';
}
function printQuota (quota) {
if (!quota) return;
let lbl;
if (quota.allow_until) lbl = 'Free quota left';
else if (quota.deny_until) lbl = 'Free quota exhausted. Unidle available in';
if (lbl) {
let timestamp = quota.allow_until ? new Date(quota.allow_until) : new Date(quota.deny_until);
let time = timeRemaining(new Date(), timestamp);
console.log(`${lbl}: ${time}`);
}
}
function printExtended (dynos) {
cli.table(dynos, {
columns: [
{key: 'id', label: 'ID'},
{key: 'name', label: 'Process'},
{key: 'state', label: 'State', format: (state, row) => `${state} ${timeAgo(new Date(row.updated_at))}`},
{key: 'extended.region', label: 'Region'},
{key: 'extended.instance', label: 'Instance'},
{key: 'extended.port', label: 'Port'},
{key: 'extended.az', label: 'AZ'},
{key: 'release.version', label: 'Release'},
{key: 'command', label: 'Command', format: trunc},
{key: 'extended.route', label: 'Route'},
{key: 'size', label: 'Size'},
]
});
}
function printDynos (dynos) {
let dynosByCommand = _.reduce(dynos, function (dynosByCommand, dyno) {
let since = timeAgo(new Date(dyno.updated_at));
let size = dyno.size || '1X';
if (dyno.type === 'run') {
let key = `run: one-off processes`;
if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
dynosByCommand[key].push(`${dyno.name} (${size}): ${dyno.state} ${since}: ${dyno.command}`);
} else {
let key = `${dyno.type} (${size}): ${dyno.command}`;
if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
let item = `${dyno.name}: ${dyno.state} ${since}`;
dynosByCommand[key].push(item);
}
return dynosByCommand;
}, {});
for (let key of Object.keys(dynosByCommand).sort()) {
cli.styledHeader(key);
for (let dyno of dynosByCommand[key]) cli.log(dyno);
cli.log();
}
}
function* run (context, heroku) {
let suffix = context.flags.extended ? '?extended=true' : '';
let data = yield {
quota: heroku.request({
path: `/apps/${context.app}/actions/get-quota${suffix}`,
method: 'post', headers: {accept: 'application/vnd.heroku+json; version=3.app-quotas'}
}).catch(() => {}),
dynos: heroku.request({path: `/apps/${context.app}/dynos${suffix}`}),
};
if (context.flags.json) {
printJSON(data);
} else if (context.flags.extended) {
printExtended(data.dynos);
} else {
printQuota(data.quota);
printDynos(data.dynos);
}
}
module.exports = {
topic: 'ps',
description: 'list dynos for an app',
flags: [
{name: 'json', description: 'display as json'},
{name: 'extended', char: 'x', hidden: true},
],
help: `
Example:
$ heroku ps
=== run: one-off dyno
run.1: up for 5m: bash
=== web: bundle exec thin start -p $PORT
web.1: created for 30s`,
needsAuth: true,
needsApp: true,
run: cli.command(co.wrap(run))
};