Skip to content

Commit f3244d8

Browse files
authored
Merge pull request #263 from zinuzoid/limitsset-support-new-format
feat(limits-cmd): accept new limits:set value type
2 parents 0e8f19e + f83362c commit f3244d8

4 files changed

Lines changed: 146 additions & 25 deletions

File tree

cmd/limits.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ func parseLimits(limits []string) (map[string]interface{}, error) {
145145
}
146146

147147
func parseLimit(limit string) (string, string, error) {
148-
regex := regexp.MustCompile("^([A-z]+)=([0-9]+[bkmgBKMG]{1,2}|[0-9.]{1,5}|[0-9.]{1,5}[m]{0,1})$")
148+
regex := regexp.MustCompile("^([A-z]+)=(([0-9]+[bkmgBKMG]{1,2}|[0-9.]{1,5}|[0-9.]{1,5}m?)(/([0-9]+[bkmgBKMG]{1,2}|[0-9.]{1,5}|[0-9.]{1,5}m?}))?)$")
149149

150150
if !regex.MatchString(limit) {
151-
return "", "", fmt.Errorf(`%s doesn't fit format type=#unit or type=#
152-
Examples: web=2G worker=500M web=300`, limit)
151+
return "", "", fmt.Errorf(`%s doesn't fit format type=#unit or type=# or type=#/#
152+
Examples: web=2G worker=500M db=1G/2G`, limit)
153153
}
154154

155155
capture := regex.FindStringSubmatch(limit)

cmd/limits_test.go

Lines changed: 122 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,26 @@ type parseLimitCase struct {
2222
func TestParseLimit(t *testing.T) {
2323
t.Parallel()
2424

25+
var errorHint = ` doesn't fit format type=#unit or type=# or type=#/#
26+
Examples: web=2G worker=500M db=1G/2G`
27+
2528
cases := []parseLimitCase{
2629
{"web=2G", "web", "2G", false, ""},
27-
{"=1", "", "", true, `=1 doesn't fit format type=#unit or type=#
28-
Examples: web=2G worker=500M web=300`},
29-
{"web=", "", "", true, `web= doesn't fit format type=#unit or type=#
30-
Examples: web=2G worker=500M web=300`},
31-
{"1=", "", "", true, `1= doesn't fit format type=#unit or type=#
32-
Examples: web=2G worker=500M web=300`},
33-
{"web=G", "", "", true, `web=G doesn't fit format type=#unit or type=#
34-
Examples: web=2G worker=500M web=300`},
30+
{"web=2", "web", "2", false, ""},
31+
{"web=100m", "web", "100m", false, ""},
32+
{"web=0.1", "web", "0.1", false, ""},
33+
{"web=.123", "web", ".123", false, ""},
34+
{"web=2G/4G", "web", "2G/4G", false, ""},
35+
{"web=2/4", "web", "2/4", false, ""},
36+
{"web=200m/400m", "web", "200m/400m", false, ""},
37+
{"web=0.2/0.4", "web", "0.2/0.4", false, ""},
38+
{"web=.2/.4", "web", ".2/.4", false, ""},
39+
{"=1", "", "", true, "=1" + errorHint},
40+
{"web=", "", "", true, "web=" + errorHint},
41+
{"1=", "", "", true, "1=" + errorHint},
42+
{"web=G", "", "", true, "web=G" + errorHint},
43+
{"web=/", "", "", true, "web=/" + errorHint},
44+
{"web=/1", "", "", true, "web=/1" + errorHint},
3545
}
3646

3747
for _, check := range cases {
@@ -58,8 +68,8 @@ func TestLimitTags(t *testing.T) {
5868

5969
cases := []parseLimitsCase{
6070
{[]string{"web=1G", "worker=2"}, map[string]interface{}{"web": "1G", "worker": "2"}, false, ""},
61-
{[]string{"foo=", "web=1G"}, nil, true, `foo= doesn't fit format type=#unit or type=#
62-
Examples: web=2G worker=500M web=300`},
71+
{[]string{"foo=", "web=1G"}, nil, true, `foo= doesn't fit format type=#unit or type=# or type=#/#
72+
Examples: web=2G worker=500M db=1G/2G`},
6373
}
6474

6575
for _, check := range cases {
@@ -88,11 +98,13 @@ func TestLimitsList(t *testing.T) {
8898
"app": "enterprise",
8999
"values": {},
90100
"memory": {
91-
"web": "2G"
101+
"web": "2G",
102+
"db": "1000M/1500M"
92103
},
93104
"cpu": {
94105
"web": "2",
95-
"worker": "1"
106+
"worker": "1",
107+
"db": "500m/2000m"
96108
},
97109
"tags": {},
98110
"registry": {},
@@ -110,9 +122,11 @@ func TestLimitsList(t *testing.T) {
110122
assert.Equal(t, b.String(), `=== enterprise Limits
111123
112124
--- Memory
125+
db 1000M/1500M
113126
web 2G
114127
115128
--- CPU
129+
db 500m/2000m
116130
web 2
117131
worker 1
118132
`, "output")
@@ -236,6 +250,102 @@ web 1G
236250
237251
--- CPU
238252
Unlimited
253+
`, "output")
254+
255+
// with requests/limit parameter
256+
server.Mux.HandleFunc("/v2/apps/jim/config/", func(w http.ResponseWriter, r *http.Request) {
257+
testutil.SetHeaders(w)
258+
if r.Method == "POST" {
259+
testutil.AssertBody(t, api.Config{
260+
Memory: map[string]interface{}{
261+
"web": "2000M",
262+
"worker": "0/3G",
263+
"db": "4G/5G",
264+
},
265+
}, r)
266+
}
267+
268+
fmt.Fprintf(w, `{
269+
"owner": "foo",
270+
"app": "jim",
271+
"values": {},
272+
"memory": {
273+
"web": "2000M",
274+
"worker": "0/3G",
275+
"db": "4G/5G"
276+
},
277+
"cpu": {},
278+
"tags": {},
279+
"registry": {},
280+
"created": "2014-01-01T00:00:00UTC",
281+
"updated": "2014-01-01T00:00:00UTC",
282+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
283+
}`)
284+
})
285+
b.Reset()
286+
287+
err = cmdr.LimitsSet("jim", []string{"web=2000M", "worker=0/3G", "db=4G/5G"}, "memory")
288+
assert.NoErr(t, err)
289+
290+
assert.Equal(t, testutil.StripProgress(b.String()), `Applying limits... done
291+
292+
=== jim Limits
293+
294+
--- Memory
295+
db 4G/5G
296+
web 2000M
297+
worker 0/3G
298+
299+
--- CPU
300+
Unlimited
301+
`, "output")
302+
303+
// with requests/limit parameter
304+
server.Mux.HandleFunc("/v2/apps/phew/config/", func(w http.ResponseWriter, r *http.Request) {
305+
testutil.SetHeaders(w)
306+
if r.Method == "POST" {
307+
testutil.AssertBody(t, api.Config{
308+
CPU: map[string]interface{}{
309+
"web": "2",
310+
"worker": "0/300m",
311+
"db": "4/5.6",
312+
},
313+
}, r)
314+
}
315+
316+
fmt.Fprintf(w, `{
317+
"owner": "foo",
318+
"app": "jim",
319+
"values": {},
320+
"cpu": {
321+
"web": "2",
322+
"worker": "0/300m",
323+
"db": "4/5.6"
324+
},
325+
"cpu": {},
326+
"tags": {},
327+
"registry": {},
328+
"created": "2014-01-01T00:00:00UTC",
329+
"updated": "2014-01-01T00:00:00UTC",
330+
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
331+
}`)
332+
})
333+
b.Reset()
334+
335+
err = cmdr.LimitsSet("phew", []string{"web=2", "worker=0/300m", "db=4/5.6"}, "cpu")
336+
assert.NoErr(t, err)
337+
338+
assert.Equal(t, testutil.StripProgress(b.String()), `Applying limits... done
339+
340+
=== phew Limits
341+
342+
--- Memory
343+
Unlimited
344+
345+
--- CPU
346+
db 4/5.6
347+
web 2
348+
worker 0/300m
239349
`, "output")
240350
}
241351

parser/limits.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,24 @@ Options:
6161

6262
func limitSet(argv []string, cmdr cmd.Commander) error {
6363
usage := `
64-
Sets resource limits for an application.
64+
Sets resource requests and limits for an application.
6565
6666
A resource limit is a finite resource within a pod which we can apply
67-
restrictions through Kubernetes. This limit is applied to each individual
68-
pod, so setting a memory limit of 1G for an application means that each
69-
pod gets 1G of memory.
67+
restrictions through Kubernetes. A resource request is used by Kubernetes scheduler
68+
to select a node that can guarantee requested resource. If provided only one value,
69+
it'll be default by Kubernetes as both request and limit. These request and limit
70+
are applied to each individual pod, so setting a memory limit of 1G for an application
71+
means that each pod gets 1G of memory. Value needs to be within 0 <= request <= limit
7072
71-
Usage: deis limits:set [options] <type>=<limit>...
73+
Usage: deis limits:set [options] <type>=<value>...
7274
7375
Arguments:
7476
<type>
7577
the process type as defined in your Procfile, such as 'web' or 'worker'.
7678
Note that Dockerfile apps have a default 'cmd' process type.
77-
<limit>
78-
The limit to apply to the process type. By default, this is set to --memory.
79+
<value>
80+
The value to apply to the process type. By default, this is set to --memory.
81+
Can be in <limit> or <request>/<limit> format eg. web=2G db=1G/2G
7982
You can only set one type of limit per call.
8083
8184
With --memory, units are represented in Bytes (B), Kilobytes (K), Megabytes
@@ -92,9 +95,9 @@ Options:
9295
-a --app=<app>
9396
the uniquely identifiable name for the application.
9497
--cpu
95-
limits CPU.
98+
value apply to CPU.
9699
-m --memory
97-
limits memory. [default: true]
100+
value apply to memory. [default: true]
98101
`
99102

100103
args, err := docopt.Parse(usage, argv, true, "", false, true)
@@ -104,7 +107,7 @@ Options:
104107
}
105108

106109
app := safeGetValue(args, "--app")
107-
limits := args["<type>=<limit>"].([]string)
110+
limits := args["<type>=<value>"].([]string)
108111
limitType := "memory"
109112

110113
if args["--cpu"].(bool) {

parser/limits_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ func TestLimits(t *testing.T) {
4949
args: []string{"limits:set", "web=1G"},
5050
expected: "",
5151
},
52+
{
53+
args: []string{"limits:set", "web=1G worker=2G"},
54+
expected: "",
55+
},
56+
{
57+
args: []string{"limits:set", "web=1G/2G worker=2G/4G"},
58+
expected: "",
59+
},
5260
{
5361
args: []string{"limits:set", "--cpu", "web=1"},
5462
expected: "",

0 commit comments

Comments
 (0)