Skip to content

Commit b7a4c6f

Browse files
committed
feat(limits): add limits plan support
1 parent 0352ebb commit b7a4c6f

8 files changed

Lines changed: 552 additions & 49 deletions

File tree

api/config.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,9 @@ type Config struct {
2424
App string `json:"app,omitempty"`
2525
// Values are exposed as environment variables to the app.
2626
Values map[string]interface{} `json:"values,omitempty"`
27-
// Memory is used to set process memory limits. The key is the process name
28-
// and the value is a number followed by a memory unit (G, M, K, or B). Ex: 200G
29-
Memory map[string]interface{} `json:"memory,omitempty"`
30-
// CPU is used to set process CPU limits. It can be set in terms of whole CPUs
31-
// (ex 1) or in milli units to reflect the number of CPU shares (ex 500m).
32-
CPU map[string]interface{} `json:"cpu,omitempty"`
27+
// Limits is used to set process resources limits. The key is the process name
28+
// and the value is a limit plan. Ex: std1.xlarge.c1m1
29+
Limits map[string]interface{} `json:"limits,omitempty"`
3330
// Timeout is used to set termination grace period. The key is the process name
3431
// and the value is a number in seconds, e.g. 30
3532
Timeout map[string]interface{} `json:"termination_grace_period,omitempty"`

api/limits.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package api
2+
3+
// LimitSpec is the definition of GET /v2/limits/specs/
4+
type LimitSpec struct {
5+
ID string `json:"id"`
6+
CPU map[string]interface{} `json:"cpu"`
7+
Memory map[string]interface{} `json:"memory"`
8+
Features map[string]interface{} `json:"features"`
9+
Keywords []string `json:"keywords"`
10+
Disabled bool `json:"disabled"`
11+
}
12+
13+
// LimitPlan is the definition of GET /v2/limits/plans/
14+
type LimitPlan struct {
15+
ID string `json:"id"`
16+
Spec LimitSpec `json:"spec"`
17+
CPU int `json:"cpu"`
18+
Memory int `json:"memory"`
19+
Features map[string]interface{} `json:"features"`
20+
Disabled bool `json:"disabled"`
21+
}

config/config_test.go

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ const configFixture string = `
2020
"TEST": "testing",
2121
"FOO": "bar"
2222
},
23-
"memory": {
24-
"web": "1G"
25-
},
26-
"cpu": {
27-
"web": "1000"
28-
},
23+
"limits": {
24+
"web": "std1.xlarge.c1m1"
25+
},
2926
"tags": {
3027
"test": "tests"
3128
},
@@ -43,8 +40,7 @@ const configUnsetFixture string = `
4340
"owner": "test",
4441
"app": "unset-test",
4542
"values": {},
46-
"memory": {},
47-
"cpu": {},
43+
"limits": {},
4844
"tags": {},
4945
"registry": {},
5046
"created": "2014-01-01T00:00:00UTC",
@@ -53,8 +49,8 @@ const configUnsetFixture string = `
5349
}
5450
`
5551

56-
const configSetExpected string = `{"values":{"FOO":"bar","TEST":"testing"},"memory":{"web":"1G"},"cpu":{"web":"1000"},"tags":{"test":"tests"},"registry":{"username":"bob"}}`
57-
const configUnsetExpected string = `{"values":{"FOO":null,"TEST":null},"memory":{"web":null},"cpu":{"web":null},"tags":{"test":null},"registry":{"username":null}}`
52+
const configSetExpected string = `{"values":{"FOO":"bar","TEST":"testing"},"limits":{"web":"std1.xlarge.c1m1"},"tags":{"test":"tests"},"registry":{"username":"bob"}}`
53+
const configUnsetExpected string = `{"values":{"FOO":null,"TEST":null},"limits":{"web":null},"tags":{"test":null},"registry":{"username":null}}`
5854

5955
type fakeHTTPServer struct{}
6056

@@ -132,11 +128,8 @@ func TestConfigSet(t *testing.T) {
132128
"TEST": "testing",
133129
"FOO": "bar",
134130
},
135-
Memory: map[string]interface{}{
136-
"web": "1G",
137-
},
138-
CPU: map[string]interface{}{
139-
"web": "1000",
131+
Limits: map[string]interface{}{
132+
"web": "std1.xlarge.c1m1",
140133
},
141134
Tags: map[string]interface{}{
142135
"test": "tests",
@@ -154,11 +147,8 @@ func TestConfigSet(t *testing.T) {
154147
"TEST": "testing",
155148
"FOO": "bar",
156149
},
157-
Memory: map[string]interface{}{
158-
"web": "1G",
159-
},
160-
CPU: map[string]interface{}{
161-
"web": "1000",
150+
Limits: map[string]interface{}{
151+
"web": "std1.xlarge.c1m1",
162152
},
163153
Tags: map[string]interface{}{
164154
"test": "tests",
@@ -195,8 +185,7 @@ func TestConfigUnset(t *testing.T) {
195185
Owner: "test",
196186
App: "unset-test",
197187
Values: map[string]interface{}{},
198-
Memory: map[string]interface{}{},
199-
CPU: map[string]interface{}{},
188+
Limits: map[string]interface{}{},
200189
Tags: map[string]interface{}{},
201190
Registry: map[string]interface{}{},
202191
Created: "2014-01-01T00:00:00UTC",
@@ -209,10 +198,7 @@ func TestConfigUnset(t *testing.T) {
209198
"TEST": nil,
210199
"FOO": nil,
211200
},
212-
Memory: map[string]interface{}{
213-
"web": nil,
214-
},
215-
CPU: map[string]interface{}{
201+
Limits: map[string]interface{}{
216202
"web": nil,
217203
},
218204
Tags: map[string]interface{}{
@@ -253,11 +239,8 @@ func TestConfigList(t *testing.T) {
253239
"TEST": "testing",
254240
"FOO": "bar",
255241
},
256-
Memory: map[string]interface{}{
257-
"web": "1G",
258-
},
259-
CPU: map[string]interface{}{
260-
"web": "1000",
242+
Limits: map[string]interface{}{
243+
"web": "std1.xlarge.c1m1",
261244
},
262245
Tags: map[string]interface{}{
263246
"test": "tests",

go.mod

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@ module github.com/drycc/controller-sdk-go
22

33
go 1.22
44

5-
require golang.org/x/net v0.21.0
5+
require (
6+
github.com/stretchr/testify v1.9.0
7+
golang.org/x/net v0.21.0
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
gopkg.in/yaml.v3 v3.0.1 // indirect
14+
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
6+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
17
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
28
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
9+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
10+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
11+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

hooks/hooks_test.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"reflect"
99
"testing"
1010

11-
"github.com/drycc/controller-sdk-go"
11+
drycc "github.com/drycc/controller-sdk-go"
1212
"github.com/drycc/controller-sdk-go/api"
1313
)
1414

@@ -26,11 +26,8 @@ const configFixture string = `
2626
"TEST": "testing",
2727
"FOO": "bar"
2828
},
29-
"memory": {
30-
"web": "1G"
31-
},
32-
"cpu": {
33-
"web": "1000"
29+
"Limits": {
30+
"web": "std1.xlarge.c1m1"
3431
},
3532
"tags": {
3633
"test": "tests"
@@ -162,11 +159,8 @@ func TestConfigHook(t *testing.T) {
162159
"TEST": "testing",
163160
"FOO": "bar",
164161
},
165-
Memory: map[string]interface{}{
166-
"web": "1G",
167-
},
168-
CPU: map[string]interface{}{
169-
"web": "1000",
162+
Limits: map[string]interface{}{
163+
"web": "std1.xlarge.c1m1",
170164
},
171165
Tags: map[string]interface{}{
172166
"test": "tests",

limits/limits.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package limits
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strings"
7+
8+
drycc "github.com/drycc/controller-sdk-go"
9+
"github.com/drycc/controller-sdk-go/api"
10+
)
11+
12+
// Specs is list all available limit specs
13+
func Specs(c *drycc.Client, keywords string, results int) ([]api.LimitSpec, int, error) {
14+
u := "/v2/limits/specs/"
15+
if keywords != "" {
16+
u += fmt.Sprintf("?keywords=%s", keywords)
17+
}
18+
19+
body, count, reqErr := c.LimitedRequest(u, results)
20+
if reqErr != nil && !drycc.IsErrAPIMismatch(reqErr) {
21+
return []api.LimitSpec{}, -1, reqErr
22+
}
23+
var limitSpecs []api.LimitSpec
24+
if err := json.Unmarshal([]byte(body), &limitSpecs); err != nil {
25+
return []api.LimitSpec{}, -1, err
26+
}
27+
return limitSpecs, count, reqErr
28+
}
29+
30+
// Plans is list all available limit plans
31+
func Plans(c *drycc.Client, specId string, cpu, memory, results int) ([]api.LimitPlan, int, error) {
32+
var queryArray []string
33+
if cpu > 0 {
34+
queryArray = append(queryArray, fmt.Sprintf("cpu=%d", cpu))
35+
}
36+
if memory > 0 {
37+
queryArray = append(queryArray, fmt.Sprintf("memory=%d", memory))
38+
}
39+
if specId != "" {
40+
queryArray = append(queryArray, fmt.Sprintf("spec-id=%s", specId))
41+
}
42+
u := "/v2/limits/plans/"
43+
if len(queryArray) > 0 {
44+
u += fmt.Sprintf("%s?%s", u, strings.Join(queryArray, "&"))
45+
}
46+
47+
body, count, reqErr := c.LimitedRequest(u, results)
48+
49+
if reqErr != nil && !drycc.IsErrAPIMismatch(reqErr) {
50+
return []api.LimitPlan{}, -1, reqErr
51+
}
52+
var limitPlans []api.LimitPlan
53+
if err := json.Unmarshal([]byte(body), &limitPlans); err != nil {
54+
return []api.LimitPlan{}, -1, err
55+
}
56+
return limitPlans, count, reqErr
57+
}
58+
59+
// GetPlan is get a available Plan
60+
func GetPlan(c *drycc.Client, planId string) (api.LimitPlan, error) {
61+
u := fmt.Sprintf("/v2/limits/plans/%s/", planId)
62+
res, reqErr := c.Request("GET", u, nil)
63+
64+
if reqErr != nil && !drycc.IsErrAPIMismatch(reqErr) {
65+
return api.LimitPlan{}, reqErr
66+
}
67+
defer res.Body.Close()
68+
limitPlan := api.LimitPlan{}
69+
if err := json.NewDecoder(res.Body).Decode(&limitPlan); err != nil {
70+
return api.LimitPlan{}, err
71+
}
72+
return limitPlan, reqErr
73+
}

0 commit comments

Comments
 (0)