Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(redis-v5/pg-v5): Update Redis/PG maintenance commands to suggest Data Maintenance CLI #2358

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
9 changes: 8 additions & 1 deletion packages/pg-v5/commands/maintenance/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ async function run(context, heroku) {
const {app, args} = context
const db = await fetcher.addon(app, args.database)

if (util.essentialPlan(db)) throw new Error('pg:maintenance is only available for production databases')
if (util.essentialPlan(db)) throw new Error('pg:maintenance isn’t available for Essential-tier databases.')

let newPluginMessage = `You can also list the maintenance events for your Postgres database with ${cli.color.cmd('data:maintenances')}.`
newPluginMessage += '\nFollow https://devcenter.heroku.com/articles/data-maintenance-cli-commands'
newPluginMessage += `\nto install the ${cli.color.bold.cyan('Data Maintenance CLI plugin')}.`

cli.warn(newPluginMessage)

let info = await heroku.get(`/client/v11/databases/${db.id}/maintenance`, {host: host(db)})
cli.log(info.message)
}
Expand Down
9 changes: 8 additions & 1 deletion packages/pg-v5/commands/maintenance/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ async function run(context, heroku) {
const db = await fetcher.addon(app, args.database)

if (util.essentialPlan(db)) throw new Error('pg:maintenance isn’t available for Essential-tier databases.')

let newPluginMessage = `You can also start a maintenance with ${cli.color.cmd('data:maintenances:run')}.`
newPluginMessage += '\nFollow https://devcenter.heroku.com/articles/data-maintenance-cli-commands'
newPluginMessage += `\nto install the ${cli.color.bold.cyan('Data Maintenance CLI plugin')}.`

cli.warn(newPluginMessage)

await cli.action(`Starting maintenance for ${cli.color.addon(db.name)}`, (async function () {
if (!flags.force) {
let appInfo = await heroku.get(`/apps/${app}`)
if (!appInfo.maintenance) throw new Error('Application must be in maintenance mode or run with --force')
if (!appInfo.maintenance) throw new Error('You must put your app in maintenance mode with maintenance:on, or use pg:maintenance:run --force to manually initiate maintenance.')
}

let response = await heroku.post(`/client/v11/databases/${db.id}/maintenance`, {host: host(db)})
Expand Down
6 changes: 6 additions & 0 deletions packages/pg-v5/commands/maintenance/window.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ async function run(context, heroku) {

if (!args.window.match(/^[A-Za-z]{2,10} \d\d?:[03]0$/)) throw new Error('Window must be "Day HH:MM" where MM is 00 or 30')

let newPluginMessage = `You can also change the maintenance window with ${cli.color.cmd('data:maintenances:window:update')}.`
newPluginMessage += '\nFollow https://devcenter.heroku.com/articles/data-maintenance-cli-commands'
newPluginMessage += `\nto install the ${cli.color.bold.cyan('Data Maintenance CLI plugin')}.`

cli.warn(newPluginMessage)

await cli.action(`Setting maintenance window for ${cli.color.addon(db.name)} to ${cli.color.cyan(args.window)}`, (async function () {
let response = await heroku.put(`/client/v11/databases/${db.id}/maintenance_window`, {
body: {description: args.window},
Expand Down
8 changes: 7 additions & 1 deletion packages/pg-v5/test/commands/maintenance/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ describe('pg:maintenance', () => {
let api
let pg

let displayed = ` ▸ You can also start a maintenance with data:maintenances:run.
▸ Follow https://devcenter.heroku.com/articles/data-maintenance-cli-commands
▸ to install the Data Maintenance CLI plugin.
`

beforeEach(() => {
api = nock('https://api.heroku.com')
pg = nock('https://postgres-api.heroku.com')
Expand All @@ -41,7 +46,8 @@ describe('pg:maintenance', () => {
api.get('/apps/myapp').reply(200, {maintenance: true})
pg.post('/client/v11/databases/1/maintenance').reply(200, {message: 'foo'})
return cmd.run({app: 'myapp', args: {}, flags: {}})
.then(() => expect(cli.stderr).to.equal('Starting maintenance for postgres-1... foo\n'))
.then(() => expect(cli.stderr).to.equal(displayed + 'Starting maintenance for postgres-1... foo\n'))

.then(() => expect(cli.stdout).to.equal(''))
})
})
8 changes: 7 additions & 1 deletion packages/pg-v5/test/commands/maintenance/window.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ describe('pg:maintenance', () => {
let api
let pg

let displayed = ` ▸ You can also change the maintenance window with
▸ data:maintenances:window:update.
▸ Follow https://devcenter.heroku.com/articles/data-maintenance-cli-commands
▸ to install the Data Maintenance CLI plugin.
`

beforeEach(() => {
api = nock('https://api.heroku.com')
pg = nock('https://postgres-api.heroku.com')
Expand All @@ -41,6 +47,6 @@ describe('pg:maintenance', () => {
pg.put('/client/v11/databases/1/maintenance_window', {description: 'Sunday 06:30'}).reply(200)
return cmd.run({app: 'myapp', args: {window: 'Sunday 06:30'}})
.then(() => expect(cli.stdout).to.equal(''))
.then(() => expect(cli.stderr).to.equal('Setting maintenance window for postgres-1 to Sunday 06:30... done\n'))
.then(() => expect(cli.stderr).to.equal(displayed + 'Setting maintenance window for postgres-1 to Sunday 06:30... done\n'))
})
})
12 changes: 9 additions & 3 deletions packages/redis-v5/commands/maintenance.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ module.exports = {
const api = require('../lib/shared')(context, heroku)
let addon = await api.getRedisAddon()

let newPluginMessage = `You can also manage maintenances on your Redis instance with ${cli.color.cmd('data:maintenances')}.`
newPluginMessage += '\nFollow https://devcenter.heroku.com/articles/data-maintenance-cli-commands'
newPluginMessage += `\nto install the ${cli.color.cyan('Data Maintenance CLI plugin')}.`

cli.warn(newPluginMessage)

// eslint-disable-next-line no-eq-null, eqeqeq
if (addon.plan.name.match(/hobby/) != null) {
cli.exit(1, 'redis:maintenance is not available for hobby-dev instances')
if (addon.plan.name.match(/mini/) != null) {
cli.exit(1, 'The redis:maintenance command is not available for Mini plans')
}

if (context.flags.window) {
Expand All @@ -38,7 +44,7 @@ module.exports = {
if (context.flags.run) {
let app = await heroku.get(`/apps/${context.app}`)
if (!app.maintenance && !context.flags.force) {
cli.exit(1, 'Application must be in maintenance mode or --force flag must be used')
cli.exit(1, 'You must put your application in maintenance mode, or use the redis:maintenance --run --force command.')
}

let maintenance = await api.request(`/redis/v0/databases/${addon.name}/maintenance`, 'POST')
Expand Down
21 changes: 13 additions & 8 deletions packages/redis-v5/test/commands/maintenance.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ describe('heroku redis:maintenance', function () {
})

describe('heroku redis:maintenance', function () {
let newPluginMessage = ' ▸ You can also manage maintenances on your Redis instance with'
newPluginMessage += '\n ▸ data:maintenances.'
newPluginMessage += '\n ▸ Follow https://devcenter.heroku.com/articles/data-maintenance-cli-commands'
newPluginMessage += '\n ▸ to install the Data Maintenance CLI plugin.\n'

beforeEach(function () {
cli.mockConsole()
nock.cleanAll()
Expand All @@ -32,7 +37,7 @@ describe('heroku redis:maintenance', function () {
.then(() => app.done())
.then(() => redis.done())
.then(() => expect(cli.stdout).to.equal('Message\n'))
.then(() => expect(cli.stderr).to.equal(''))
.then(() => expect(cli.stderr).to.equal(newPluginMessage))
})

it('# sets the maintenance window', function () {
Expand All @@ -50,7 +55,7 @@ describe('heroku redis:maintenance', function () {
.then(() => app.done())
.then(() => redis.done)
.then(() => expect(cli.stdout).to.equal('Maintenance window for redis-haiku (REDIS_FOO, REDIS_BAR) set to Mon 10:00.\n'))
.then(() => expect(cli.stderr).to.equal(''))
.then(() => expect(cli.stderr).to.equal(newPluginMessage))
})

it('# runs the maintenance', function () {
Expand All @@ -70,7 +75,7 @@ describe('heroku redis:maintenance', function () {
.then(() => appInfo.done())
.then(() => redis.done())
.then(() => expect(cli.stdout).to.equal('Message\n'))
.then(() => expect(cli.stderr).to.equal(''))
.then(() => expect(cli.stderr).to.equal(newPluginMessage))
})

it('# run errors out when not in maintenance', function () {
Expand All @@ -86,17 +91,17 @@ describe('heroku redis:maintenance', function () {
.then(() => app.done())
.then(() => appInfo.done())
.then(() => expect(cli.stdout).to.equal(''))
.then(() => expect(unwrap(cli.stderr)).to.equal('Application must be in maintenance mode or --force flag must be used\n'))
.then(() => expect(unwrap(cli.stderr)).to.include('You must put your application in maintenance mode, or use the redis:maintenance --run --force command.\n'))
})

it('# errors out on hobby dynos', function () {
it('# errors out on mini instances', function () {
let app = nock('https://api.heroku.com:443')
.get('/apps/example/addons').reply(200, [
{name: 'redis-haiku', addon_service: {name: 'heroku-redis'}, plan: {name: 'hobby'}, config_vars: ['REDIS_FOO', 'REDIS_BAR']},
{name: 'redis-haiku', addon_service: {name: 'heroku-redis'}, plan: {name: 'mini'}, config_vars: ['REDIS_FOO', 'REDIS_BAR']},
])

return expect(command.run({app: 'example', args: {}, auth: {username: 'foobar', password: 'password'}})).to.be.rejected
.then(() => expect(unwrap(cli.stderr)).to.equal('redis:maintenance is not available for hobby-dev instances\n'))
.then(() => expect(unwrap(cli.stderr)).to.include('The redis:maintenance command is not available for Mini plans\n'))
.then(() => app.done())
})

Expand All @@ -109,6 +114,6 @@ describe('heroku redis:maintenance', function () {
return expect(command.run({app: 'example', args: {}, flags: {window: 'Mon 10:45'}, auth: {username: 'foobar', password: 'password'}})).to.be.rejected
.then(() => app.done())
.then(() => expect(cli.stdout).to.equal(''))
.then(() => expect(unwrap(cli.stderr)).to.equal('Maintenance windows must be "Day HH:MM", where MM is 00 or 30.\n'))
.then(() => expect(unwrap(cli.stderr)).to.include('Maintenance windows must be "Day HH:MM", where MM is 00 or 30.\n'))
})
})