github.com/drycc/workflow-cli@v1.5.3-0.20240322092846-d4ee25983af9/cmd/limits.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/drycc/controller-sdk-go/api" 8 "github.com/drycc/controller-sdk-go/config" 9 "github.com/drycc/controller-sdk-go/limits" 10 "github.com/drycc/workflow-cli/settings" 11 ) 12 13 // LimitsList lists an app's limits. 14 func (d *DryccCmd) LimitsList(appID string) error { 15 s, appID, err := load(d.ConfigFile, appID) 16 17 if err != nil { 18 return err 19 } 20 21 config, err := config.List(s.Client, appID) 22 if d.checkAPICompatibility(s.Client, err) != nil { 23 return err 24 } 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"] 42 table.Append([]string{ 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), 48 }) 49 } 50 51 table.Render() 52 } else { 53 d.Println(fmt.Sprintf("No limits found in %s app.", appID)) 54 } 55 return nil 56 } 57 58 // LimitsSet sets an app's limits. 59 func (d *DryccCmd) LimitsSet(appID string, limits []string) error { 60 s, appID, err := load(d.ConfigFile, appID) 61 62 if err != nil { 63 return err 64 } 65 66 configObj := api.Config{} 67 if len(limits) > 0 { 68 limitsMap, err := parseLimits(limits) 69 if err != nil { 70 return err 71 } 72 configObj.Limits = limitsMap 73 } 74 75 d.Print("Applying limits... ") 76 77 quit := progress(d.WOut) 78 79 _, err = config.Set(s.Client, appID, configObj) 80 quit <- true 81 <-quit 82 if d.checkAPICompatibility(s.Client, err) != nil { 83 return err 84 } 85 86 d.Print("done\n\n") 87 88 return d.LimitsList(appID) 89 } 90 91 // LimitsUnset removes an app's limits. 92 func (d *DryccCmd) LimitsUnset(appID string, limits []string) error { 93 s, appID, err := load(d.ConfigFile, appID) 94 95 if err != nil { 96 return err 97 } 98 99 d.Print("Applying limits... ") 100 101 quit := progress(d.WOut) 102 103 configObj := api.Config{} 104 if len(limits) > 0 { 105 limitsMap := make(map[string]interface{}) 106 for _, limit := range limits { 107 limitsMap[limit] = nil 108 } 109 configObj.Limits = limitsMap 110 } 111 112 _, err = config.Set(s.Client, appID, configObj) 113 quit <- true 114 <-quit 115 if d.checkAPICompatibility(s.Client, err) != nil { 116 return err 117 } 118 119 d.Print("done\n\n") 120 121 return d.LimitsList(appID) 122 } 123 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 198 func parseLimits(limits []string) (map[string]interface{}, error) { 199 limitsMap := make(map[string]interface{}) 200 201 for _, limit := range limits { 202 key, value, err := parseLimit(limit) 203 204 if err != nil { 205 return nil, err 206 } 207 208 limitsMap[key] = value 209 } 210 211 return limitsMap, nil 212 } 213 214 func parseLimit(limit string) (string, string, error) { 215 regex := regexp.MustCompile("^([a-z0-9]+(?:-[a-z0-9]+)*)=([-.a-zA-Z0-9]+)$") 216 217 if !regex.MatchString(limit) { 218 return "", "", fmt.Errorf(`%s doesn't fit format type=#unit or type=# 219 Examples: web=std1.large.c1m1`, limit) 220 } 221 222 capture := regex.FindStringSubmatch(limit) 223 224 return capture[1], capture[2], nil 225 }