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  }