github.com/drycc/workflow-cli@v1.5.3-0.20240322092846-d4ee25983af9/cmd/resources.go (about)

     1  package cmd
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"os"
     7  	"regexp"
     8  	"strconv"
     9  
    10  	"sigs.k8s.io/yaml"
    11  
    12  	"github.com/drycc/controller-sdk-go/api"
    13  	"github.com/drycc/controller-sdk-go/resources"
    14  	"github.com/drycc/workflow-cli/settings"
    15  )
    16  
    17  // ResourceServices list resource service
    18  func (d *DryccCmd) ResourcesServices(results int) error {
    19  	s, err := settings.Load(d.ConfigFile)
    20  
    21  	if err != nil {
    22  		return err
    23  	}
    24  
    25  	if results == defaultLimit {
    26  		results = s.Limit
    27  	}
    28  	services, count, err := resources.Services(s.Client, results)
    29  	if d.checkAPICompatibility(s.Client, err) != nil {
    30  		return err
    31  	}
    32  
    33  	if count == 0 {
    34  		d.Println("Could not find any services")
    35  	} else {
    36  		table := d.getDefaultFormatTable([]string{"ID", "NAME", "UPDATEABLE"})
    37  		for _, service := range services {
    38  			table.Append([]string{
    39  				service.ID,
    40  				service.Name,
    41  				strconv.FormatBool(service.Updateable),
    42  			})
    43  		}
    44  		table.Render()
    45  	}
    46  	return nil
    47  }
    48  
    49  // ResourcePlans list resource plans
    50  func (d *DryccCmd) ResourcesPlans(serviceName string, results int) error {
    51  	s, err := settings.Load(d.ConfigFile)
    52  
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	if results == defaultLimit {
    58  		results = s.Limit
    59  	}
    60  	plans, count, err := resources.Plans(s.Client, serviceName, results)
    61  	if d.checkAPICompatibility(s.Client, err) != nil {
    62  		return err
    63  	}
    64  
    65  	if count == 0 {
    66  		d.Println(fmt.Sprintf("Could not find any plans in %s service.", serviceName))
    67  	} else {
    68  		table := d.getDefaultFormatTable([]string{"ID", "NAME", "DESCRIPTION"})
    69  		for _, plan := range plans {
    70  			table.Append([]string{
    71  				plan.ID,
    72  				plan.Name,
    73  				plan.Description,
    74  			})
    75  		}
    76  		table.Render()
    77  	}
    78  	return nil
    79  }
    80  
    81  // ResourcesCreate create a resource for the application
    82  func (d *DryccCmd) ResourcesCreate(appID, plan string, name string, params []string, values string) error {
    83  	s, appID, err := load(d.ConfigFile, appID)
    84  
    85  	if err != nil {
    86  		return err
    87  	}
    88  	paramsMap := make(map[string]interface{})
    89  	if values != "" {
    90  		valueFile, err := os.Stat(values)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		if valueFile.Size() == 0 {
    95  			return fmt.Errorf("%s is empty", values)
    96  		}
    97  		rawValues, err := os.ReadFile(values)
    98  		if err != nil {
    99  			return err
   100  		}
   101  		parsed := make(map[string]interface{})
   102  		err = yaml.Unmarshal(rawValues, &parsed)
   103  		if err != nil {
   104  			return err
   105  		}
   106  		paramsMap["rawValues"] = base64.StdEncoding.EncodeToString([]byte(rawValues))
   107  	}
   108  
   109  	d.Printf("Creating %s to %s... ", name, appID)
   110  
   111  	paramsMap, err = parseParams(paramsMap, params)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	quit := progress(d.WOut)
   117  	resource := api.Resource{
   118  		Name:    name,
   119  		Plan:    plan,
   120  		Options: paramsMap,
   121  	}
   122  	_, err = resources.Create(s.Client, appID, resource)
   123  	quit <- true
   124  	<-quit
   125  	if d.checkAPICompatibility(s.Client, err) != nil {
   126  		return err
   127  	}
   128  
   129  	d.Println("done")
   130  	return nil
   131  }
   132  
   133  // ResourcesList list resources in the application
   134  func (d *DryccCmd) ResourcesList(appID string, results int) error {
   135  	s, appID, err := load(d.ConfigFile, appID)
   136  
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	if results == defaultLimit {
   142  		results = s.Limit
   143  	}
   144  	resources, count, err := resources.List(s.Client, appID, results)
   145  	if d.checkAPICompatibility(s.Client, err) != nil {
   146  		return err
   147  	}
   148  
   149  	if count == 0 {
   150  		d.Println(fmt.Sprintf("No resources found in %s app.", appID))
   151  	} else {
   152  		table := d.getDefaultFormatTable([]string{"UUID", "NAME", "OWNER", "PLAN", "UPDATED"})
   153  		for _, resource := range resources {
   154  			table.Append([]string{
   155  				resource.UUID,
   156  				resource.Name,
   157  				resource.Owner,
   158  				resource.Plan,
   159  				resource.Updated,
   160  			})
   161  		}
   162  		table.Render()
   163  	}
   164  	return nil
   165  }
   166  
   167  // ResourceGet describe a resource from the application
   168  func (d *DryccCmd) ResourceGet(appID, name string) error {
   169  	s, appID, err := load(d.ConfigFile, appID)
   170  
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	//d.Printf(" %s from %s... ", name, appID)
   176  
   177  	resource, err := resources.Get(s.Client, appID, name)
   178  	if d.checkAPICompatibility(s.Client, err) != nil {
   179  		return err
   180  	}
   181  	table := d.getDefaultFormatTable([]string{})
   182  	table.Append([]string{"App:", appID})
   183  	table.Append([]string{"UUID:", resource.UUID})
   184  	table.Append([]string{"Name:", resource.Name})
   185  	table.Append([]string{"Plan:", resource.Plan})
   186  	table.Append([]string{"Owner:", resource.Owner})
   187  	table.Append([]string{"Status:", resource.Status})
   188  	table.Append([]string{"Binding:", resource.Binding})
   189  	table.Append([]string{"Data:"})
   190  	for _, key := range *sortKeys(resource.Data) {
   191  		table.Append([]string{"", fmt.Sprintf("%s:", key), fmt.Sprintf("%s", resource.Data[key])})
   192  	}
   193  	table.Append([]string{"Options:"})
   194  	for _, key := range *sortKeys(resource.Options) {
   195  		table.Append([]string{"", fmt.Sprintf("%s:", key), fmt.Sprintf("%s", resource.Options[key])})
   196  	}
   197  	table.Append([]string{"Message:", safeGetString(resource.Message)})
   198  	table.Append([]string{"Created:", resource.Created})
   199  	table.Append([]string{"Updated:", resource.Updated})
   200  	table.Render()
   201  	return nil
   202  }
   203  
   204  // ResourceDelete delete a resource from the application
   205  func (d *DryccCmd) ResourceDelete(appID, name, confirm string) error {
   206  	s, appID, err := load(d.ConfigFile, appID)
   207  
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	if confirm == "" {
   213  		d.Printf(` !    WARNING: Potentially Destructive Action
   214   !    This command will destroy the resource: %s
   215   !    To proceed, type "%s" or re-run this command with --confirm=%s
   216  
   217  > `, name, name, name)
   218  
   219  		fmt.Scanln(&confirm)
   220  	}
   221  
   222  	if confirm != name {
   223  		return fmt.Errorf("resource %s does not match confirm %s, aborting", name, confirm)
   224  	}
   225  
   226  	d.Printf("Destroying %s...\n", name)
   227  
   228  	quit := progress(d.WOut)
   229  	err = resources.Delete(s.Client, appID, name)
   230  	quit <- true
   231  	<-quit
   232  	if d.checkAPICompatibility(s.Client, err) != nil {
   233  		return err
   234  	}
   235  
   236  	d.Println("done")
   237  	return nil
   238  }
   239  
   240  // ResourcePut update a resource for the application
   241  func (d *DryccCmd) ResourcePut(appID, plan string, name string, params []string, values string) error {
   242  	s, appID, err := load(d.ConfigFile, appID)
   243  
   244  	if err != nil {
   245  		return err
   246  	}
   247  	paramsMap := make(map[string]interface{})
   248  	if values != "" {
   249  		valueFile, err := os.Stat(values)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		if valueFile.Size() == 0 {
   254  			return fmt.Errorf("%s is empty", values)
   255  		}
   256  		rawValues, err := os.ReadFile(values)
   257  		if err != nil {
   258  			return err
   259  		}
   260  		parsed := make(map[string]interface{})
   261  		err = yaml.Unmarshal(rawValues, &parsed)
   262  		if err != nil {
   263  			return err
   264  		}
   265  		paramsMap["rawValues"] = base64.StdEncoding.EncodeToString([]byte(rawValues))
   266  	}
   267  
   268  	d.Printf("Updating %s to %s... ", name, appID)
   269  
   270  	paramsMap, err = parseParams(paramsMap, params)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	quit := progress(d.WOut)
   276  	resource := api.Resource{
   277  		Plan:    plan,
   278  		Options: paramsMap,
   279  	}
   280  	_, err = resources.Put(s.Client, appID, name, resource)
   281  	quit <- true
   282  	<-quit
   283  	if d.checkAPICompatibility(s.Client, err) != nil {
   284  		return err
   285  	}
   286  
   287  	d.Println("done")
   288  	return nil
   289  }
   290  
   291  // ResourceBind mount a resource to process of the application
   292  func (d *DryccCmd) ResourceBind(appID string, name string) error {
   293  	s, appID, err := load(d.ConfigFile, appID)
   294  
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	d.Print("Binding resource... ")
   300  
   301  	quit := progress(d.WOut)
   302  	bindAction := api.ResourceBinding{BindAction: "bind"}
   303  	_, err = resources.Binding(s.Client, appID, name, bindAction)
   304  	quit <- true
   305  	<-quit
   306  	if d.checkAPICompatibility(s.Client, err) != nil {
   307  		return err
   308  	}
   309  
   310  	d.Print("done\n")
   311  
   312  	return nil
   313  }
   314  
   315  // ResourceUnbind resource a resource the application
   316  func (d *DryccCmd) ResourceUnbind(appID string, name string) error {
   317  	s, appID, err := load(d.ConfigFile, appID)
   318  
   319  	if err != nil {
   320  		return err
   321  	}
   322  
   323  	d.Print("Unbinding resource... ")
   324  
   325  	quit := progress(d.WOut)
   326  	bindAction := api.ResourceBinding{BindAction: "unbind"}
   327  	_, err = resources.Binding(s.Client, appID, name, bindAction)
   328  	quit <- true
   329  	<-quit
   330  	if d.checkAPICompatibility(s.Client, err) != nil {
   331  		return err
   332  	}
   333  
   334  	d.Print("done\n")
   335  
   336  	return nil
   337  }
   338  
   339  // parseParams transfer params to map
   340  func parseParams(paramsMap map[string]interface{}, params []string) (map[string]interface{}, error) {
   341  	regex := regexp.MustCompile(`^([A-z_]+[A-z0-9_]*[\.{1}[A-z0-9_]+]*)=([\s\S]*)$`)
   342  	for _, param := range params {
   343  		if regex.MatchString(param) {
   344  			captures := regex.FindStringSubmatch(param)
   345  			paramsMap[captures[1]] = captures[2]
   346  		} else {
   347  			return nil, fmt.Errorf("'%s' does not match the pattern 'key=var', ex: MODE=test", param)
   348  		}
   349  	}
   350  
   351  	return paramsMap, nil
   352  }