Skip to content

Commit d4ee259

Browse files
committed
feat(limits): add limits plan support
1 parent c790fd9 commit d4ee259

14 files changed

Lines changed: 531 additions & 260 deletions

.woodpecker/build-linux.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
platform: linux/amd64
2-
31
labels:
42
type: exec
3+
platform: linux/amd64
54

6-
pipeline:
5+
steps:
76
- name: build-linux
87
image: bash
98
commands:

.woodpecker/test-linux.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ matrix:
33
- linux/amd64
44
- linux/arm64
55

6-
platform: ${platform}
7-
86
labels:
97
type: exec
8+
platform: ${platform}
109

11-
pipeline:
10+
steps:
1211
- name: test-linux
1312
image: bash
1413
commands:

cmd/auth_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ func TestLogin(t *testing.T) {
4040
w.Write(nil)
4141
})
4242
err = cmdr.Login(server.Server.URL, false)
43-
fmt.Println(err)
44-
fmt.Println(t)
4543
assert.NoError(t, err)
4644
}
4745

cmd/cmd.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ type Commander interface {
6969
LabelsSet(string, []string) error
7070
LabelsUnset(string, []string) error
7171
LimitsList(string) error
72-
LimitsSet(string, []string, []string) error
73-
LimitsUnset(string, []string, []string) error
72+
LimitsSet(string, []string) error
73+
LimitsUnset(string, []string) error
74+
LimitsSpecs(string, int) error
75+
LimitsPlans(string, int, int, int) error
7476
TimeoutsList(string) error
7577
TimeoutsSet(string, []string) error
7678
TimeoutsUnset(string, []string) error
@@ -122,7 +124,7 @@ type Commander interface {
122124
ResourcesPlans(string, int) error
123125
ResourcesCreate(string, string, string, []string, string) error
124126
ResourcesList(string, int) error
125-
ResourceDelete(string, string) error
127+
ResourceDelete(string, string, string) error
126128
ResourceGet(string, string) error
127129
ResourcePut(string, string, string, []string, string) error
128130
ResourceBind(string, string) error

cmd/limits.go

Lines changed: 111 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
"github.com/drycc/controller-sdk-go/api"
88
"github.com/drycc/controller-sdk-go/config"
9+
"github.com/drycc/controller-sdk-go/limits"
10+
"github.com/drycc/workflow-cli/settings"
911
)
1012

1113
// LimitsList lists an app's limits.
@@ -20,26 +22,32 @@ func (d *DryccCmd) LimitsList(appID string) error {
2022
if d.checkAPICompatibility(s.Client, err) != nil {
2123
return err
2224
}
23-
if len(config.CPU) > 0 || len(config.Memory) > 0 {
24-
table := d.getDefaultFormatTable([]string{"UUID", "OWNER", "PTYPE", "DEVICE", "QUOTA"})
25-
for _, key := range *sortKeys(config.Memory) {
25+
cached := make(map[string]api.LimitPlan)
26+
27+
if len(config.Limits) > 0 {
28+
table := d.getDefaultFormatTable([]string{"PTYPE", "PLAN", "VCPUS", "MEMORY", "FEATURES"})
29+
for _, ptype := range *sortKeys(config.Limits) {
30+
limitPlanID := fmt.Sprintf("%v", config.Limits[ptype])
31+
if _, ok := cached[limitPlanID]; !ok {
32+
limitPlan, err := limits.GetPlan(s.Client, limitPlanID)
33+
if err != nil {
34+
return err
35+
}
36+
cached[limitPlanID] = limitPlan
37+
}
38+
limitPlan := cached[limitPlanID]
39+
gpuCount := limitPlan.Features["gpu"]
40+
gpuName := limitPlan.Spec.Features["gpu"].(map[string]interface{})["name"]
41+
gpuMemory := limitPlan.Spec.Features["gpu"].(map[string]interface{})["memory"].(map[string]interface{})["size"]
2642
table.Append([]string{
27-
config.UUID,
28-
config.Owner,
29-
key,
30-
"MEM",
31-
fmt.Sprintf("%v", config.Memory[key]),
32-
})
33-
}
34-
for _, key := range *sortKeys(config.CPU) {
35-
table.Append([]string{
36-
config.UUID,
37-
config.Owner,
38-
key,
39-
"CPU",
40-
fmt.Sprintf("%v", config.CPU[key]),
43+
ptype,
44+
limitPlanID,
45+
fmt.Sprintf("%v", limitPlan.CPU),
46+
fmt.Sprintf("%v GiB", limitPlan.Memory),
47+
fmt.Sprintf("%v %v * %v", gpuName, gpuMemory, gpuCount),
4148
})
4249
}
50+
4351
table.Render()
4452
} else {
4553
d.Println(fmt.Sprintf("No limits found in %s app.", appID))
@@ -48,27 +56,20 @@ func (d *DryccCmd) LimitsList(appID string) error {
4856
}
4957

5058
// LimitsSet sets an app's limits.
51-
func (d *DryccCmd) LimitsSet(appID string, cpuLimits []string, memoryLimits []string) error {
59+
func (d *DryccCmd) LimitsSet(appID string, limits []string) error {
5260
s, appID, err := load(d.ConfigFile, appID)
5361

5462
if err != nil {
5563
return err
5664
}
5765

5866
configObj := api.Config{}
59-
if len(cpuLimits) > 0 {
60-
cpuLimitsMap, err := parseLimits(cpuLimits)
61-
if err != nil {
62-
return err
63-
}
64-
configObj.CPU = cpuLimitsMap
65-
}
66-
if len(memoryLimits) > 0 {
67-
memoryLimitsMap, err := parseLimits(memoryLimits)
67+
if len(limits) > 0 {
68+
limitsMap, err := parseLimits(limits)
6869
if err != nil {
6970
return err
7071
}
71-
configObj.Memory = memoryLimitsMap
72+
configObj.Limits = limitsMap
7273
}
7374

7475
d.Print("Applying limits... ")
@@ -88,7 +89,7 @@ func (d *DryccCmd) LimitsSet(appID string, cpuLimits []string, memoryLimits []st
8889
}
8990

9091
// LimitsUnset removes an app's limits.
91-
func (d *DryccCmd) LimitsUnset(appID string, cpuLimits []string, memoryLimits []string) error {
92+
func (d *DryccCmd) LimitsUnset(appID string, limits []string) error {
9293
s, appID, err := load(d.ConfigFile, appID)
9394

9495
if err != nil {
@@ -100,19 +101,12 @@ func (d *DryccCmd) LimitsUnset(appID string, cpuLimits []string, memoryLimits []
100101
quit := progress(d.WOut)
101102

102103
configObj := api.Config{}
103-
if len(cpuLimits) > 0 {
104-
cpuMap := make(map[string]interface{})
105-
for _, limit := range cpuLimits {
106-
cpuMap[limit] = nil
107-
}
108-
configObj.CPU = cpuMap
109-
}
110-
if len(memoryLimits) > 0 {
111-
memoryMap := make(map[string]interface{})
112-
for _, limit := range memoryLimits {
113-
memoryMap[limit] = nil
104+
if len(limits) > 0 {
105+
limitsMap := make(map[string]interface{})
106+
for _, limit := range limits {
107+
limitsMap[limit] = nil
114108
}
115-
configObj.Memory = memoryMap
109+
configObj.Limits = limitsMap
116110
}
117111

118112
_, err = config.Set(s.Client, appID, configObj)
@@ -127,6 +121,80 @@ func (d *DryccCmd) LimitsUnset(appID string, cpuLimits []string, memoryLimits []
127121
return d.LimitsList(appID)
128122
}
129123

124+
// LimitsSpecs list limit spec
125+
func (d *DryccCmd) LimitsSpecs(keywords string, results int) error {
126+
s, err := settings.Load(d.ConfigFile)
127+
128+
if err != nil {
129+
return err
130+
}
131+
132+
if results == defaultLimit {
133+
results = s.Limit
134+
}
135+
limitSpecs, count, err := limits.Specs(s.Client, keywords, results)
136+
if d.checkAPICompatibility(s.Client, err) != nil {
137+
return err
138+
}
139+
if count == 0 {
140+
d.Println("Could not find any limit spec.")
141+
} else {
142+
table := d.getDefaultFormatTable([]string{"ID", "CPU", "CLOCK", "BOOST", "CORES", "THREADS", "NETWORK", "FEATURES"})
143+
for _, limitSpec := range limitSpecs {
144+
gpuName := limitSpec.Features["gpu"].(map[string]interface{})["name"]
145+
gpuMemory := limitSpec.Features["gpu"].(map[string]interface{})["memory"].(map[string]interface{})["size"]
146+
table.Append([]string{
147+
limitSpec.ID,
148+
fmt.Sprintf("%v", limitSpec.CPU["name"]),
149+
fmt.Sprintf("%v", limitSpec.CPU["clock"]),
150+
fmt.Sprintf("%v", limitSpec.CPU["boost"]),
151+
fmt.Sprintf("%v", limitSpec.CPU["cores"]),
152+
fmt.Sprintf("%v", limitSpec.CPU["threads"]),
153+
fmt.Sprintf("%v", limitSpec.Features["network"]),
154+
fmt.Sprintf("%v %v", gpuName, gpuMemory),
155+
})
156+
}
157+
table.Render()
158+
}
159+
return nil
160+
}
161+
162+
// LimitsPlans list limit plan
163+
func (d *DryccCmd) LimitsPlans(specID string, cpu, memory, results int) error {
164+
s, err := settings.Load(d.ConfigFile)
165+
166+
if err != nil {
167+
return err
168+
}
169+
170+
if results == defaultLimit {
171+
results = s.Limit
172+
}
173+
limitPlans, count, err := limits.Plans(s.Client, specID, cpu, memory, results)
174+
if d.checkAPICompatibility(s.Client, err) != nil {
175+
return err
176+
}
177+
if count == 0 {
178+
d.Println("Could not find any limit spec.")
179+
} else {
180+
table := d.getDefaultFormatTable([]string{"ID", "SPEC", "CPU", "VCPUS", "MEMORY", "FEATURES"})
181+
for _, limitPlan := range limitPlans {
182+
gpuName := limitPlan.Spec.Features["gpu"].(map[string]interface{})["name"]
183+
gpuMemory := limitPlan.Spec.Features["gpu"].(map[string]interface{})["memory"].(map[string]interface{})["size"]
184+
table.Append([]string{
185+
limitPlan.ID,
186+
limitPlan.Spec.ID,
187+
fmt.Sprintf("%v", limitPlan.Spec.CPU["name"]),
188+
fmt.Sprintf("%v", limitPlan.CPU),
189+
fmt.Sprintf("%v GiB", limitPlan.Memory),
190+
fmt.Sprintf("%v %v", gpuName, gpuMemory),
191+
})
192+
}
193+
table.Render()
194+
}
195+
return nil
196+
}
197+
130198
func parseLimits(limits []string) (map[string]interface{}, error) {
131199
limitsMap := make(map[string]interface{})
132200

@@ -144,11 +212,11 @@ func parseLimits(limits []string) (map[string]interface{}, error) {
144212
}
145213

146214
func parseLimit(limit string) (string, string, error) {
147-
regex := regexp.MustCompile("^([a-z0-9]+(?:-[a-z0-9]+)*)=(([1-9][0-9]*[mgMG]|[1-9][0-9]*m?))$")
215+
regex := regexp.MustCompile("^([a-z0-9]+(?:-[a-z0-9]+)*)=([-.a-zA-Z0-9]+)$")
148216

149217
if !regex.MatchString(limit) {
150218
return "", "", fmt.Errorf(`%s doesn't fit format type=#unit or type=#
151-
Examples: web=2G worker=500M db=1G`, limit)
219+
Examples: web=std1.large.c1m1`, limit)
152220
}
153221

154222
capture := regex.FindStringSubmatch(limit)

0 commit comments

Comments
 (0)