heroku
Version:
CLI to interact with Heroku
85 lines (84 loc) • 4.21 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const color_1 = require("@heroku-cli/color");
const command_1 = require("@heroku-cli/command");
const core_1 = require("@oclif/core");
const confirmCommand_1 = require("../../../lib/confirmCommand");
const fetcher_1 = require("../../../lib/pg/fetcher");
const host_1 = require("../../../lib/pg/host");
const util_1 = require("../../../lib/pg/util");
const nls_1 = require("../../../nls");
class Rotate extends command_1.Command {
async run() {
const { flags, args } = await this.parse(Rotate);
const { app, all, confirm, name, force } = flags;
const { addon: db } = await (0, fetcher_1.getAttachment)(this.heroku, app, args.database);
const warnings = [];
const cred = name || 'default';
if (all && name !== undefined) {
throw new Error('cannot pass both --all and --name');
}
if ((0, util_1.legacyEssentialPlan)(db) && cred !== 'default') {
throw new Error('Legacy Essential-tier databases do not support named credentials.');
}
if (all && force) {
warnings.push('This forces rotation on all credentials including the default credential.');
}
let { body: attachments } = await this.heroku.get(`/addons/${db.name}/addon-attachments`);
if (name) {
attachments = attachments.filter(a => a.namespace === `credential:${cred}`);
}
if (!all) {
warnings.push(`The password for the ${cred} credential will rotate.`);
}
if (all || force || cred === 'default') {
warnings.push('Connections will be reset and applications will be restarted.');
}
else {
warnings.push('Connections older than 30 minutes will be reset, and a temporary rotation username will be used during the process.');
}
if (force) {
warnings.push(`Any followers lagging in replication (see ${color_1.default.cyan.bold('heroku pg:info')}) will be inaccessible until caught up.`);
}
if (attachments.length > 0) {
const uniqueAttachments = [...new Set(attachments.map(attachment => color_1.default.app(attachment.app.name || '')))]
.sort()
.join(', ');
warnings.push(`This command will affect the app${(attachments.length > 1) ? 's' : ''} ${uniqueAttachments}.`);
}
await (0, confirmCommand_1.default)(app, confirm, `Destructive Action\n${warnings.join('\n')}`);
const options = {
hostname: (0, host_1.default)(),
body: { forced: force !== null && force !== void 0 ? force : undefined },
headers: {
Authorization: `Basic ${Buffer.from(`:${this.heroku.auth}`).toString('base64')}`,
},
};
if (all) {
core_1.ux.action.start(`Rotating all credentials on ${color_1.default.yellow(db.name)}`);
await this.heroku.post(`/postgres/v0/databases/${db.name}/credentials_rotation`, options);
}
else {
core_1.ux.action.start(`Rotating ${cred} on ${color_1.default.yellow(db.name)}`);
await this.heroku.post(`/postgres/v0/databases/${db.name}/credentials/${encodeURIComponent(cred)}/credentials_rotation`, options);
}
core_1.ux.action.stop();
}
}
exports.default = Rotate;
Rotate.topic = 'pg';
Rotate.description = 'rotate the database credentials';
Rotate.flags = {
name: command_1.flags.string({
char: 'n',
description: 'which credential to rotate (default credentials if not specified and --all is not used)',
}),
all: command_1.flags.boolean({ description: 'rotate all credentials', exclusive: ['name'] }),
confirm: command_1.flags.string({ char: 'c' }),
force: command_1.flags.boolean({ description: 'forces rotating the targeted credentials' }),
app: command_1.flags.app({ required: true }),
remote: command_1.flags.remote(),
};
Rotate.args = {
database: core_1.Args.string({ description: `${(0, nls_1.nls)('pg:database:arg:description')} ${(0, nls_1.nls)('pg:database:arg:description:default:suffix')}` }),
};
;