Skip to content

Commit 2aab84d

Browse files
committed
feat(limits-cmd): accept new limits:set value type
Accept new cli format limits:set web=500m/2000m Value is map to requests/limits parameter in Kubernetes resource management. See workflow#562
1 parent 50451f8 commit 2aab84d

4 files changed

Lines changed: 137 additions & 17 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: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,14 @@ func limitUnset(argv []string, cmdr cmd.Commander) error {
118118
usage := `
119119
Unsets resource limits for an application.
120120
121-
Usage: deis limits:unset [options] [--memory | --cpu] <type>...
121+
Usage: deis limits:unset [options] [--memory | --cpu] <type>=<value>...
122122
123123
Arguments:
124124
<type>
125125
the process type as defined in your Procfile, such as 'web' or 'worker'.
126126
Note that Dockerfile apps have a default 'cmd' process type.
127+
<value>
128+
the number of limits or requests/limits value.
127129
128130
Options:
129131
-a --app=<app>
@@ -141,7 +143,7 @@ Options:
141143
}
142144

143145
app := safeGetValue(args, "--app")
144-
limits := args["<type>"].([]string)
146+
limits := args["<type>=<value>"].([]string)
145147
limitType := "memory"
146148

147149
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)