From c062c590956afe38c60ea57982ccc28b51b2c5b5 Mon Sep 17 00:00:00 2001 From: Jon Daniel Date: Fri, 1 Mar 2024 17:52:53 -0500 Subject: [PATCH] feat(pg:upgrade): support essential dbs (#2637) * Proof-of-concept on changes to support essential pg:upgrade This code is still a work-in-progress but I am just trying to get an idea of what changes would be required in the CLI for us to support pg:upgrade on the essential tier. * WIP update tests * WIP update tests waiting on feedback Waiting for feedback before finalizaing updated tests * Update tests * Update copy * Update smoke tests * WIP fix smoke tests * Update api call from PR feedback * Add comment for custom stripAnsi function --------- Co-authored-by: Zane Whitfield --- .../cli/test/acceptance/commands-output.ts | 2 +- .../test/acceptance/smoke.acceptance.test.ts | 11 +++++++- packages/pg-v5/commands/upgrade.js | 28 +++++++++---------- .../test/unit/commands/upgrade.unit.test.js | 16 ++--------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/cli/test/acceptance/commands-output.ts b/packages/cli/test/acceptance/commands-output.ts index a49e81d7a2..9a7d4ddb7a 100644 --- a/packages/cli/test/acceptance/commands-output.ts +++ b/packages/cli/test/acceptance/commands-output.ts @@ -208,7 +208,7 @@ export default `\u001B[1m Command Summary pg:settings:log-statement log_statement controls which SQL statements are logged. pg:settings:track-functions track_functions controls tracking of function call counts and time used. Default is none. pg:unfollow stop a replica from following and make it a writeable database - pg:upgrade unfollow a database and upgrade it to the latest stable PostgreSQL version + pg:upgrade For an Essential-* plan, this command upgrades the database to the latest stable PostgreSQL version. For a Standard-tier and higher plan, this command unfollows the parent database before upgrading to the latest stable PostgreSQL version. pg:vacuum-stats show dead rows and whether an automatic vacuum is expected to be triggered pg:wait blocks until database is available pipelines list pipelines you have access to diff --git a/packages/cli/test/acceptance/smoke.acceptance.test.ts b/packages/cli/test/acceptance/smoke.acceptance.test.ts index 243c0f5f78..7dbdb206c6 100755 --- a/packages/cli/test/acceptance/smoke.acceptance.test.ts +++ b/packages/cli/test/acceptance/smoke.acceptance.test.ts @@ -7,6 +7,15 @@ import * as qq from 'qqjs' import commandsOutput from './commands-output' +// this is a custom function that strips both ansi characters and several additional characters +const stripAnsi = (input: string) => { + // eslint-disable-next-line no-control-regex, unicorn/escape-case + const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\s|─/g + const cleanedString = input.replace(ansiRegex, '') + + return cleanedString +} + const globby = require('globby') const app = 'heroku-cli-ci-smoke-test-app' @@ -193,7 +202,7 @@ describe('@acceptance smoke tests', () => { // this test will fail when run locally depending on which plugins you have installed it('heroku commands', async () => { const {stdout} = await run('commands') - expect(stdout).to.equal(commandsOutput) + expect(stripAnsi(stdout)).to.equal(stripAnsi(commandsOutput)) }) it('asserts monorepo plugins are in core', async () => { diff --git a/packages/pg-v5/commands/upgrade.js b/packages/pg-v5/commands/upgrade.js index 070bd6b23a..e642f0e70d 100644 --- a/packages/pg-v5/commands/upgrade.js +++ b/packages/pg-v5/commands/upgrade.js @@ -9,25 +9,23 @@ async function run(context, heroku) { let {app, args, flags} = context let db = await fetcher.addon(app, args.database) - if (util.essentialPlan(db)) throw new Error('pg:upgrade is only available for follower databases on at least the Standard tier.') + if (util.legacyEssentialPlan(db)) throw new Error('pg:upgrade is only available for Essential-* databases and follower databases on Standard-tier and higher plans.') - let [replica, status] = await Promise.all([ - heroku.get(`/client/v11/databases/${db.id}`, {host: host(db)}), - heroku.get(`/client/v11/databases/${db.id}/upgrade_status`, {host: host(db)}), - ]) + const replica = await heroku.get(`/client/v11/databases/${db.id}`, {host: host(db)}) - if (status.error) throw new Error(status.error) + if (replica.following) { + let origin = util.databaseNameFromUrl(replica.following, await heroku.get(`/apps/${app}/config-vars`)) - if (!replica.following) { - throw new Error('pg:upgrade is only available for follower databases on at least the Standard tier.') - } - - let origin = util.databaseNameFromUrl(replica.following, await heroku.get(`/apps/${app}/config-vars`)) + await cli.confirmApp(app, flags.confirm, `WARNING: Destructive action + ${cli.color.addon(db.name)} will be upgraded to a newer PostgreSQL version, stop following ${origin}, and become writable. - await cli.confirmApp(app, flags.confirm, `WARNING: Destructive action -${cli.color.addon(db.name)} will be upgraded to a newer PostgreSQL version, stop following ${origin}, and become writable. + This cannot be undone.`) + } else { + await cli.confirmApp(app, flags.confirm, `WARNING: Destructive action +${cli.color.addon(db.name)} will be upgraded to a newer PostgreSQL version. -This cannot be undone.`) + This cannot be undone.`) + } let data = {version: flags.version} @@ -40,7 +38,7 @@ This cannot be undone.`) module.exports = { topic: 'pg', command: 'upgrade', - description: 'unfollow a database and upgrade it to the latest stable PostgreSQL version', + description: 'For an Essential-* plan, this command upgrades the database to the latest stable PostgreSQL version. For a Standard-tier and higher plan, this command unfollows the parent database before upgrading to the latest stable PostgreSQL version.', help: 'to upgrade to another PostgreSQL version, use pg:copy instead', needsApp: true, needsAuth: true, diff --git a/packages/pg-v5/test/unit/commands/upgrade.unit.test.js b/packages/pg-v5/test/unit/commands/upgrade.unit.test.js index 17abf02461..3b7ca2f5d8 100644 --- a/packages/pg-v5/test/unit/commands/upgrade.unit.test.js +++ b/packages/pg-v5/test/unit/commands/upgrade.unit.test.js @@ -39,25 +39,16 @@ describe('pg:upgrade', () => { pg.done() }) - it('refuses to upgrade essential dbs', () => { - addon.plan = {name: 'heroku-postgresql:hobby-dev'} + it('refuses to upgrade legacy essential dbs', () => { + addon.plan = {name: 'heroku-postgresql:basic'} return expect(cmd.run({app: 'myapp', args: {}, flags: {confirm: 'myapp'}})) - .to.be.rejectedWith(Error, 'pg:upgrade is only available for follower databases on at least the Standard tier.') - }) - - it('refuses to upgrade non-follower dbs', () => { - pg.get('/client/v11/databases/1').reply(200, {forked_from: 'postgres://db1'}) - pg.get('/client/v11/databases/1/upgrade_status').reply(200, {}) - - return expect(cmd.run({app: 'myapp', args: {}, flags: {confirm: 'myapp'}})) - .to.be.rejectedWith(Error, 'pg:upgrade is only available for follower databases on at least the Standard tier.') + .to.be.rejectedWith(Error, 'pg:upgrade is only available for Essential-* databases and follower databases on Standard-tier and higher plans.') }) it('upgrades db', () => { api.get('/apps/myapp/config-vars').reply(200, {DATABASE_URL: 'postgres://db1'}) pg.get('/client/v11/databases/1').reply(200, {following: 'postgres://db1'}) - pg.get('/client/v11/databases/1/upgrade_status').reply(200, {}) pg.post('/client/v11/databases/1/upgrade').reply(200) return cmd.run({app: 'myapp', args: {}, flags: {confirm: 'myapp'}}) .then(() => expect(cli.stderr).to.equal('Starting upgrade of postgres-1... heroku pg:wait to track status\n')) @@ -66,7 +57,6 @@ describe('pg:upgrade', () => { it('upgrades db with version flag', () => { api.get('/apps/myapp/config-vars').reply(200, {DATABASE_URL: 'postgres://db1'}) pg.get('/client/v11/databases/1').reply(200, {following: 'postgres://db1'}) - pg.get('/client/v11/databases/1/upgrade_status').reply(200, {}) pg.post('/client/v11/databases/1/upgrade').reply(200) return cmd.run({app: 'myapp', args: {}, flags: {confirm: 'myapp', version: '9.6'}}) .then(() => expect(cli.stderr).to.equal('Starting upgrade of postgres-1... heroku pg:wait to track status\n'))