-
Notifications
You must be signed in to change notification settings - Fork 214
/
scale.js
111 lines (94 loc) · 3.79 KB
/
scale.js
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
99
100
101
102
103
104
105
106
107
108
109
110
111
'use strict'
let cli = require('heroku-cli-util')
const {compact} = require('lodash')
let emptyFormationErr = app => {
return new Error(`No process types on ${cli.color.app(app)}.
Upload a Procfile to add process types.
https://devcenter.heroku.com/articles/procfile`)
}
async function run(context, heroku) {
let app = context.app
// will remove this flag once we have
// successfully launched larger dyno sizes
let isLargerDyno = false
const largerDynoFeatureFlag = await heroku.get('/account/features/frontend-larger-dynos')
.catch(error => {
if (error.statusCode === 404) {
return {enabled: false}
}
throw error
})
async function parse(args) {
// checks for larger dyno sizes
// if the feature is not enabled
if (!largerDynoFeatureFlag.enabled) {
if (args.find(a => a.match(/=/))) {
// eslint-disable-next-line array-callback-return
compact(args.map(arg => {
let match = arg.match(/^([\w-]+)([=+-]\d+)(?::([\w-]+))?$/)
if (match === null) return
let size = match[3]
const largerDynoNames = /^(?!standard-[12]x$)(performance|private|shield)-(l-ram|xl|2xl)$/i
isLargerDyno = largerDynoNames.test(size)
if (isLargerDyno) {
const availableDynoSizes = 'eco, basic, standard-1x, standard-2x, performance-m, performance-l, private-s, private-m, private-l, shield-s, shield-m, shield-l'
throw new Error(`No such size as ${size}. Use ${availableDynoSizes}.`)
}
}))
}
}
return compact(args.map(arg => {
let change = arg.match(/^([\w-]+)([=+-]\d+)(?::([\w-]+))?$/)
if (!change) return
let quantity = change[2][0] === '=' ? change[2].slice(1) : change[2]
if (change[3]) change[3] = change[3].replace('Shield-', 'Private-')
return {type: change[1], quantity, size: change[3]}
}))
}
let changes = await parse(context.args)
if (changes.length === 0) {
let formation = await heroku.get(`/apps/${app}/formation`)
const appProps = await heroku.get(`/apps/${app}`)
const shielded = appProps.space && appProps.space.shield
if (shielded) {
formation.forEach(d => {
d.size = d.size.replace('Private-', 'Shield-')
})
}
if (formation.length === 0) throw emptyFormationErr(app)
cli.log(formation.map(d => `${d.type}=${d.quantity}:${d.size}`).sort().join(' '))
} else {
await cli.action('Scaling dynos', {success: false}, (async function () {
let formation = await heroku.request({method: 'PATCH', path: `/apps/${app}/formation`, body: {updates: changes}})
const appProps = await heroku.get(`/apps/${app}`)
const shielded = appProps.space && appProps.space.shield
if (shielded) {
formation.forEach(d => {
d.size = d.size.replace('Private-', 'Shield-')
})
}
let output = formation.filter(f => changes.find(c => c.type === f.type))
.map(d => `${cli.color.green(d.type)} at ${d.quantity}:${d.size}`)
cli.action.done(`done, now running ${output.join(', ')}`)
})())
}
}
let cmd = {
variableArgs: true,
description: 'scale dyno quantity up or down',
help: `Appending a size (eg. web=2:Standard-2X) allows simultaneous scaling and resizing.
Omitting any arguments will display the app's current dyno formation, in a
format suitable for passing back into ps:scale.`,
examples: `$ heroku ps:scale web=3:Standard-2X worker+1
Scaling dynos... done, now running web at 3:Standard-2X, worker at 1:Standard-1X.
$ heroku ps:scale
web=3:Standard-2X worker=1:Standard-1X`,
needsAuth: true,
needsApp: true,
run: cli.command(run),
}
module.exports = [
Object.assign({topic: 'ps', command: 'scale'}, cmd),
Object.assign({topic: 'dyno', command: 'scale'}, cmd),
Object.assign({topic: 'scale', hidden: true}, cmd),
]