|
| 1 | +import color from '@heroku-cli/color' |
| 2 | +import {Command, flags} from '@heroku-cli/command' |
| 3 | +import {Args, ux} from '@oclif/core' |
| 4 | +import heredoc from 'tsheredoc' |
| 5 | +import {getAddon} from '../../../lib/pg/fetcher' |
| 6 | +import pgHost from '../../../lib/pg/host' |
| 7 | +import {legacyEssentialPlan, databaseNameFromUrl, essentialNumPlan, formatResponseWithCommands} from '../../../lib/pg/util' |
| 8 | +import {PgDatabase, PgUpgradeError, PgUpgradeResponse} from '../../../lib/pg/types' |
| 9 | +import * as Heroku from '@heroku-cli/schema' |
| 10 | +import confirmCommand from '../../../lib/confirmCommand' |
| 11 | +import {nls} from '../../../nls' |
| 12 | + |
| 13 | +export default class Upgrade extends Command { |
| 14 | + static topic = 'pg'; |
| 15 | + static description = heredoc(` |
| 16 | + On Essential-tier databases, this command upgrades the database's Postgres version. |
| 17 | +
|
| 18 | + On Standard-tier and higher leader databases, this command runs a previously scheduled Postgres version upgrade. You must run ${color.cmd('pg:upgrade:prepare')} before this command to schedule a version upgrade. |
| 19 | +
|
| 20 | + On follower databases, this command unfollows the leader database before upgrading the follower's Postgres version. |
| 21 | + `) |
| 22 | + |
| 23 | + static examples = [ |
| 24 | + heredoc` |
| 25 | + # Upgrade an Essential-tier database to a specific version |
| 26 | + $ heroku pg:upgrade:run postgresql-curved-12345 --version 14 --app myapp |
| 27 | + `, |
| 28 | + heredoc` |
| 29 | + # Upgrade a Standard-tier follower database to the latest supported version |
| 30 | + $ heroku pg:upgrade:run HEROKU_POSTGRESQL_BLUE_URL --app myapp |
| 31 | + `, |
| 32 | + heredoc` |
| 33 | + # Run a previously scheduled upgrade on a Standard-tier leader database |
| 34 | + $ heroku pg:upgrade:run DATABASE_URL --app myapp |
| 35 | + `, |
| 36 | + ] |
| 37 | + |
| 38 | + static flags = { |
| 39 | + confirm: flags.string({char: 'c'}), |
| 40 | + version: flags.string({char: 'v', description: 'Postgres version to upgrade to'}), |
| 41 | + app: flags.app({required: true}), |
| 42 | + remote: flags.remote({char: 'r'}), |
| 43 | + } |
| 44 | + |
| 45 | + static args = { |
| 46 | + database: Args.string({description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`}), |
| 47 | + } |
| 48 | + |
| 49 | + public async run(): Promise<void> { |
| 50 | + const {flags, args} = await this.parse(Upgrade) |
| 51 | + const {app, version, confirm} = flags |
| 52 | + const {database} = args |
| 53 | + |
| 54 | + const db = await getAddon(this.heroku, app, database) |
| 55 | + if (legacyEssentialPlan(db)) |
| 56 | + ux.error(`You can only use ${color.cmd('pg:upgrade:*')} commands on Essential-* and higher plans.`) |
| 57 | + |
| 58 | + const versionPhrase = version ? heredoc(`Postgres version ${version}`) : heredoc('the latest supported Postgres version') |
| 59 | + const {body: replica} = await this.heroku.get<PgDatabase>(`/client/v11/databases/${db.id}`, {hostname: pgHost()}) |
| 60 | + |
| 61 | + if (essentialNumPlan(db)) { |
| 62 | + await confirmCommand(app, confirm, heredoc(` |
| 63 | + Destructive action |
| 64 | + You're upgrading ${color.addon(db.name)} to ${versionPhrase}. |
| 65 | +
|
| 66 | + You can't undo this action. |
| 67 | + `)) |
| 68 | + } else if (replica.following) { |
| 69 | + const {body: configVars} = await this.heroku.get<Heroku.ConfigVars>(`/apps/${app}/config-vars`) |
| 70 | + const origin = databaseNameFromUrl(replica.following, configVars) |
| 71 | + |
| 72 | + await confirmCommand(app, confirm, heredoc(` |
| 73 | + Destructive action |
| 74 | + You're upgrading ${color.addon(db.name)} to ${versionPhrase}. The database will stop following ${origin} and become writable. |
| 75 | +
|
| 76 | + You can't undo this action. |
| 77 | + `)) |
| 78 | + } else { |
| 79 | + await confirmCommand(app, confirm, heredoc(` |
| 80 | + Destructive action |
| 81 | + You're upgrading the Postgres version on ${color.addon(db.name)}. This action also upgrades any followers on the database. |
| 82 | +
|
| 83 | + You can't undo this action. |
| 84 | + `)) |
| 85 | + } |
| 86 | + |
| 87 | + try { |
| 88 | + const data = {version} |
| 89 | + ux.action.start(`Starting upgrade on ${color.addon(db.name)}`) |
| 90 | + const response = await this.heroku.post<PgUpgradeResponse>(`/client/v11/databases/${db.id}/upgrade/run`, {hostname: pgHost(), body: data}) |
| 91 | + ux.action.stop(heredoc(`done\n${formatResponseWithCommands(response.body.message)}`)) |
| 92 | + } catch (error) { |
| 93 | + if (error instanceof Error && 'body' in error) { |
| 94 | + const response = error as PgUpgradeError |
| 95 | + ux.error(heredoc(`${formatResponseWithCommands(response.body.message)}\n\nError ID: ${response.body.id}`)) |
| 96 | + } else { |
| 97 | + throw error |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | +} |
0 commit comments