-
Notifications
You must be signed in to change notification settings - Fork 228
/
Copy pathinfo.ts
98 lines (86 loc) · 3.23 KB
/
info.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import color from '@heroku-cli/color'
import {Command, flags} from '@heroku-cli/command'
import {Args, ux} from '@oclif/core'
import pgHost from '../../../lib/pg/host'
import pgBackupsApi from '../../../lib/pg/backups'
import {sortBy} from 'lodash'
import type {BackupTransfer} from '../../../lib/pg/types'
function status(backup: BackupTransfer) {
if (backup.succeeded) {
if (backup.warnings > 0)
return `Finished with ${backup.warnings} warnings`
return 'Completed'
}
if (backup.canceled_at)
return 'Canceled'
if (backup.finished_at)
return 'Failed'
if (backup.started_at)
return 'Running'
return 'Pending'
}
function compression(compressed: number, total: number) {
let pct = 0
if (compressed > 0) {
pct = Math.round((total - compressed) / total * 100)
pct = Math.max(0, pct)
}
return ` (${pct}% compression)`
}
export default class Info extends Command {
static topic = 'pg';
static description = 'get information about a specific backup';
static flags = {
app: flags.app({required: true}),
remote: flags.remote(),
};
static args = {
backup_id: Args.string({description: 'ID of the backup. If omitted, we use the last backup ID.'}),
};
getBackup = async (id: string | undefined, app: string) => {
let backupID
if (id) {
const {num} = pgBackupsApi(app, this.heroku)
backupID = await num(id)
if (!backupID)
throw new Error(`Invalid ID: ${id}`)
} else {
let {body: transfers} = await this.heroku.get<BackupTransfer[]>(`/client/v11/apps/${app}/transfers`, {hostname: pgHost()})
transfers = sortBy(transfers, 'created_at')
const backups = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'gof3r')
const lastBackup = backups.pop()
if (!lastBackup)
throw new Error(`No backups. Capture one with ${color.cyan.bold('heroku pg:backups:capture')}`)
backupID = lastBackup.num
}
const {body: backup} = await this.heroku.get<BackupTransfer>(`/client/v11/apps/${app}/transfers/${backupID}?verbose=true`, {hostname: pgHost()})
return backup
}
displayBackup = (backup: BackupTransfer, app: string) => {
const {filesize, name} = pgBackupsApi(app, this.heroku)
ux.styledHeader(`Backup ${color.cyan(name(backup))}`)
ux.styledObject({
Database: color.green(backup.from_name),
'Started at': backup.started_at,
'Finished at': backup.finished_at,
Status: status(backup),
Type: backup.schedule ? 'Scheduled' : 'Manual', 'Original DB Size': filesize(backup.source_bytes),
'Backup Size': `${filesize(backup.processed_bytes)}${backup.finished_at ? compression(backup.processed_bytes, backup.source_bytes) : ''}`,
}, ['Database', 'Started at', 'Finished at', 'Status', 'Type', 'Original DB Size', 'Backup Size'])
ux.log()
}
displayLogs = (backup: BackupTransfer) => {
ux.styledHeader('Backup Logs')
for (const log of backup.logs)
ux.log(`${log.created_at} ${log.message}`)
ux.log()
}
public async run(): Promise<void> {
const {flags, args} = await this.parse(Info)
const {app} = flags
const {backup_id} = args
const backup = await this.getBackup(backup_id, app)
this.displayBackup(backup, app)
this.displayLogs(backup)
}
}