Skip to content

Commit 150c99f

Browse files
author
Joshua Anderson
committed
feat(client-go): add limits endpoint
1 parent 56b5860 commit 150c99f

3 files changed

Lines changed: 309 additions & 0 deletions

File tree

client-go/cmd/limits.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
8+
"github.com/deis/deis/pkg/prettyprint"
9+
10+
"github.com/deis/deis/client-go/controller/api"
11+
"github.com/deis/deis/client-go/controller/models/config"
12+
)
13+
14+
// LimitsList lists an app's limits.
15+
func LimitsList(appID string) error {
16+
c, appID, err := load(appID)
17+
18+
if err != nil {
19+
return err
20+
}
21+
22+
config, err := config.List(c, appID)
23+
24+
fmt.Printf("=== %s Limits\n\n", appID)
25+
26+
fmt.Println("--- Memory")
27+
if len(config.Memory) == 0 {
28+
fmt.Println("Unlimited")
29+
} else {
30+
memoryMap := make(map[string]string)
31+
32+
for key, value := range config.Memory {
33+
memoryMap[key] = value.(string)
34+
}
35+
36+
fmt.Print(prettyprint.PrettyTabs(memoryMap, 5))
37+
}
38+
39+
fmt.Println("\n--- CPU")
40+
if len(config.CPU) == 0 {
41+
fmt.Println("Unlimited")
42+
} else {
43+
cpuMap := make(map[string]string)
44+
45+
for key, value := range config.CPU {
46+
cpuMap[key] = strconv.Itoa(int(value.(float64)))
47+
}
48+
49+
fmt.Print(prettyprint.PrettyTabs(cpuMap, 5))
50+
}
51+
52+
return nil
53+
}
54+
55+
// LimitsSet sets an app's limits.
56+
func LimitsSet(appID string, limits []string, limitType string) error {
57+
c, appID, err := load(appID)
58+
59+
if err != nil {
60+
return err
61+
}
62+
63+
limitsMap := parseLimits(limits)
64+
65+
fmt.Print("Applying limits... ")
66+
67+
quit := progress()
68+
configObj := api.Config{}
69+
70+
if limitType == "cpu" {
71+
configObj.CPU = limitsMap
72+
} else {
73+
configObj.Memory = limitsMap
74+
}
75+
76+
_, err = config.Set(c, appID, configObj)
77+
78+
quit <- true
79+
<-quit
80+
81+
if err != nil {
82+
return err
83+
}
84+
85+
fmt.Print("done\n\n")
86+
87+
return LimitsList(appID)
88+
}
89+
90+
// LimitsUnset removes an app's limits.
91+
func LimitsUnset(appID string, limits []string, limitType string) error {
92+
c, appID, err := load(appID)
93+
94+
if err != nil {
95+
return err
96+
}
97+
98+
fmt.Print("Applying limits... ")
99+
100+
quit := progress()
101+
102+
configObj := api.Config{}
103+
104+
valuesMap := make(map[string]interface{})
105+
106+
for _, limit := range limits {
107+
valuesMap[limit] = nil
108+
}
109+
110+
if limitType == "cpu" {
111+
configObj.CPU = valuesMap
112+
} else {
113+
configObj.Memory = valuesMap
114+
}
115+
116+
_, err = config.Set(c, appID, configObj)
117+
118+
quit <- true
119+
<-quit
120+
121+
if err != nil {
122+
return err
123+
}
124+
125+
fmt.Print("done\n\n")
126+
127+
return LimitsList(appID)
128+
}
129+
130+
func parseLimits(limits []string) map[string]interface{} {
131+
limitsMap := make(map[string]interface{})
132+
133+
for _, limit := range limits {
134+
key, value, err := parseLimit(limit)
135+
136+
if err != nil {
137+
fmt.Println(err)
138+
continue
139+
}
140+
141+
limitsMap[key] = value
142+
}
143+
144+
return limitsMap
145+
}
146+
147+
func parseLimit(limit string) (string, string, error) {
148+
regex := regexp.MustCompile("^([A-z]+)=([0-9]+[BKMG]{1}|[0-9]{1,4})$")
149+
150+
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)
153+
}
154+
155+
capture := regex.FindStringSubmatch(limit)
156+
157+
return capture[1], capture[2], nil
158+
}

client-go/deis.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ Use 'git push deis master' to deploy to an application.
8989
err = parser.Domains(argv)
9090
case "builds":
9191
err = parser.Builds(argv)
92+
case "limits":
93+
err = parser.Limits(argv)
9294
case "tags":
9395
err = parser.Tags(argv)
9496
case "keys":

client-go/parser/limits.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package parser
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/deis/deis/client-go/cmd"
7+
docopt "github.com/docopt/docopt-go"
8+
)
9+
10+
// Limits routes limits commands to their specific function
11+
func Limits(argv []string) error {
12+
usage := `
13+
Valid commands for limits:
14+
15+
limits:list list resource limits for an app
16+
limits:set set resource limits for an app
17+
limits:unset unset resource limits for an app
18+
19+
Use 'deis help [command]' to learn more.
20+
`
21+
if len(argv) < 2 {
22+
return limitsList([]string{"limits:list"})
23+
}
24+
25+
switch argv[1] {
26+
case "list":
27+
return limitsList(combineCommand(argv))
28+
case "set":
29+
return limitSet(combineCommand(argv))
30+
case "unset":
31+
return limitUnset(combineCommand(argv))
32+
case "--help":
33+
fmt.Print(usage)
34+
return nil
35+
default:
36+
PrintUsage()
37+
return nil
38+
}
39+
}
40+
41+
func limitsList(argv []string) error {
42+
usage := `
43+
Lists resource limits for an application.
44+
45+
Usage: deis limits:list [options]
46+
47+
Options:
48+
-a --app=<app>
49+
the uniquely identifiable name of the application.
50+
`
51+
52+
args, err := docopt.Parse(usage, argv, true, "", false, true)
53+
54+
if err != nil {
55+
return err
56+
}
57+
58+
return cmd.LimitsList(safeGetValue(args, "--app"))
59+
}
60+
61+
func limitSet(argv []string) error {
62+
usage := `
63+
Sets resource limits for an application.
64+
65+
A resource limit is a finite resource within a container which we can apply
66+
restrictions to either through the scheduler or through the Docker API. This limit
67+
is applied to each individual container, so setting a memory limit of 1G for an
68+
application means that each container gets 1G of memory.
69+
70+
Usage: deis limits:set [options] <type>=<limit>...
71+
72+
Arguments:
73+
<type>
74+
the process type as defined in your Procfile, such as 'web' or 'worker'.
75+
Note that Dockerfile apps have a default 'cmd' process type.
76+
<limit>
77+
The limit to apply to the process type. By default, this is set to --memory.
78+
You can only set one type of limit per call.
79+
80+
With --memory, units are represented in Bytes (B), Kilobytes (K), Megabytes
81+
(M), or Gigabytes (G). For example, 'deis limit:set cmd=1G' will restrict all
82+
"cmd" processes to a maximum of 1 Gigabyte of memory each.
83+
84+
With --cpu, units are represented in the number of cpu shares. For example,
85+
'deis limit:set --cpu cmd=1024' will restrict all "cmd" processes to a
86+
maximum of 1024 cpu shares.
87+
88+
Options:
89+
-a --app=<app>
90+
the uniquely identifiable name for the application.
91+
-c --cpu
92+
limits cpu shares.
93+
-m --memory
94+
limits memory. [default: true]
95+
`
96+
97+
args, err := docopt.Parse(usage, argv, true, "", false, true)
98+
99+
if err != nil {
100+
return err
101+
}
102+
103+
app := safeGetValue(args, "--app")
104+
limits := args["<type>=<limit>"].([]string)
105+
limitType := "memory"
106+
107+
if args["--cpu"].(bool) {
108+
limitType = "cpu"
109+
}
110+
111+
return cmd.LimitsSet(app, limits, limitType)
112+
}
113+
114+
func limitUnset(argv []string) error {
115+
usage := `
116+
Unsets resource limits for an application.
117+
118+
Usage: deis limits:unset [options] [--memory | --cpu] <type>...
119+
120+
Arguments:
121+
<type>
122+
the process type as defined in your Procfile, such as 'web' or 'worker'.
123+
Note that Dockerfile apps have a default 'cmd' process type.
124+
125+
Options:
126+
-a --app=<app>
127+
the uniquely identifiable name for the application.
128+
-c --cpu
129+
limits cpu shares.
130+
-m --memory
131+
limits memory. [default: true]
132+
`
133+
134+
args, err := docopt.Parse(usage, argv, true, "", false, true)
135+
136+
if err != nil {
137+
return err
138+
}
139+
140+
app := safeGetValue(args, "--app")
141+
limits := args["<type>"].([]string)
142+
limitType := "memory"
143+
144+
if args["--cpu"].(bool) {
145+
limitType = "cpu"
146+
}
147+
148+
return cmd.LimitsUnset(app, limits, limitType)
149+
}

0 commit comments

Comments
 (0)