1 | 'use strict'
|
2 |
|
3 | const co = require('co')
|
4 | const cli = require('heroku-cli-util')
|
5 |
|
6 | function status (backup) {
|
7 | if (backup.succeeded) {
|
8 | if (backup.warnings > 0) return `Finished with ${backup.warnings} warnings`
|
9 | else return 'Completed'
|
10 | }
|
11 | if (backup.canceled_at) return 'Canceled'
|
12 | if (backup.finished_at) return 'Failed'
|
13 | if (backup.started_at) return 'Running'
|
14 | return 'Pending'
|
15 | }
|
16 |
|
17 | function compression (compressed, total) {
|
18 | let pct = 0
|
19 | if (compressed > 0) {
|
20 | pct = Math.round((total - compressed) / total * 100.0)
|
21 | pct = Math.max(0, pct)
|
22 | }
|
23 | return ` (${pct}% compression)`
|
24 | }
|
25 |
|
26 | function * run (context, heroku) {
|
27 | const pgbackups = require('../../lib/pgbackups')(context, heroku)
|
28 | const sortBy = require('lodash.sortby')
|
29 | const host = require('../../lib/host')()
|
30 | const app = context.app
|
31 |
|
32 | let getBackup = co.wrap(function * (id) {
|
33 | let backupID
|
34 | if (id) {
|
35 | backupID = yield pgbackups.transfer.num(id)
|
36 | if (!backupID) throw new Error(`Invalid ID: ${id}`)
|
37 | } else {
|
38 | let transfers = yield heroku.get(`/client/v11/apps/${app}/transfers`, {host})
|
39 | transfers = sortBy(transfers, 'created_at')
|
40 | let backups = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'gof3r')
|
41 | let lastBackup = backups.pop()
|
42 | if (!lastBackup) throw new Error(`No backups. Capture one with ${cli.color.cmd('heroku pg:backups:capture')}`)
|
43 | backupID = lastBackup.num
|
44 | }
|
45 | return yield heroku.get(`/client/v11/apps/${app}/transfers/${backupID}?verbose=true`, {host})
|
46 | })
|
47 |
|
48 | let displayBackup = backup => {
|
49 | cli.styledHeader(`Backup ${cli.color.cyan(pgbackups.transfer.name(backup))}`)
|
50 | cli.styledObject({
|
51 | 'Database': cli.color.configVar(backup.from_name),
|
52 | 'Started at': backup.started_at,
|
53 | 'Finished at': backup.finished_at,
|
54 | 'Status': status(backup),
|
55 | 'Type': backup.schedule ? 'Scheduled' : 'Manual',
|
56 | 'Original DB Size': pgbackups.filesize(backup.source_bytes),
|
57 | 'Backup Size': `${pgbackups.filesize(backup.processed_bytes)}${backup.finished_at ? compression(backup.processed_bytes, backup.source_bytes) : ''}`
|
58 | }, ['Database', 'Started at', 'Finished at', 'Status', 'Type', 'Original DB Size', 'Backup Size'])
|
59 | cli.log()
|
60 | }
|
61 |
|
62 | let displayLogs = backup => {
|
63 | cli.styledHeader('Backup Logs')
|
64 | for (let log of backup.logs) cli.log(`${log.created_at} ${log.message}`)
|
65 | cli.log()
|
66 | }
|
67 |
|
68 | let backup = yield getBackup(context.args.backup_id)
|
69 | displayBackup(backup)
|
70 | displayLogs(backup)
|
71 | }
|
72 |
|
73 | module.exports = {
|
74 | topic: 'pg',
|
75 | command: 'backups:info',
|
76 | description: 'get information about a specific backup',
|
77 | needsApp: true,
|
78 | needsAuth: true,
|
79 | args: [{name: 'backup_id', optional: true}],
|
80 | run: cli.command({preauth: true}, co.wrap(run))
|
81 | }
|