github.com/hernad/nomad@v1.6.112/command/recommendation_list.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package command
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"strings"
    10  
    11  	"github.com/hernad/nomad/api"
    12  	"github.com/mitchellh/cli"
    13  	"github.com/posener/complete"
    14  )
    15  
    16  // Ensure RecommendationListCommand satisfies the cli.Command interface.
    17  var _ cli.Command = &RecommendationListCommand{}
    18  
    19  // RecommendationListCommand implements cli.Command.
    20  type RecommendationListCommand struct {
    21  	Meta
    22  }
    23  
    24  // Help satisfies the cli.Command Help function.
    25  func (r *RecommendationListCommand) Help() string {
    26  	helpText := `
    27  Usage: nomad recommendation list [options]
    28  
    29    List is used to list the available recommendations.
    30  
    31    When ACLs are enabled, this command requires a token with the 'submit-job',
    32    'read-job', and 'submit-recommendation' capabilities for the namespace being
    33    queried.
    34  
    35  General Options:
    36  
    37    ` + generalOptionsUsage(usageOptsDefault) + `
    38  
    39  Recommendation List Options:
    40  
    41    -job
    42      Specifies the job ID to filter the recommendations list by.
    43  
    44    -group
    45      Specifies the task group name to filter within a job. If specified, the -job
    46      flag must also be specified.
    47  
    48    -task
    49      Specifies the task name to filter within a job and task group. If specified,
    50      the -job and -group flags must also be specified.
    51  
    52    -json
    53      Output the recommendations in JSON format.
    54  
    55    -t
    56      Format and display the recommendations using a Go template.
    57  `
    58  	return strings.TrimSpace(helpText)
    59  }
    60  
    61  // Synopsis satisfies the cli.Command Synopsis function.
    62  func (r *RecommendationListCommand) Synopsis() string {
    63  	return "Display all Nomad recommendations"
    64  }
    65  
    66  func (r *RecommendationListCommand) AutocompleteFlags() complete.Flags {
    67  	return mergeAutocompleteFlags(r.Meta.AutocompleteFlags(FlagSetClient),
    68  		complete.Flags{
    69  			"-job":   complete.PredictNothing,
    70  			"-group": complete.PredictNothing,
    71  			"-task":  complete.PredictNothing,
    72  			"-json":  complete.PredictNothing,
    73  			"-t":     complete.PredictAnything,
    74  		})
    75  }
    76  
    77  // Name returns the name of this command.
    78  func (r *RecommendationListCommand) Name() string { return "recommendation list" }
    79  
    80  // Run satisfies the cli.Command Run function.
    81  func (r *RecommendationListCommand) Run(args []string) int {
    82  	var json bool
    83  	var tmpl, job, group, task string
    84  
    85  	flags := r.Meta.FlagSet(r.Name(), FlagSetClient)
    86  	flags.Usage = func() { r.Ui.Output(r.Help()) }
    87  	flags.BoolVar(&json, "json", false, "")
    88  	flags.StringVar(&tmpl, "t", "", "")
    89  	flags.StringVar(&job, "job", "", "")
    90  	flags.StringVar(&group, "group", "", "")
    91  	flags.StringVar(&task, "task", "", "")
    92  	if err := flags.Parse(args); err != nil {
    93  		return 1
    94  	}
    95  
    96  	if args = flags.Args(); len(args) > 0 {
    97  		r.Ui.Error("This command takes no arguments")
    98  		r.Ui.Error(commandErrorText(r))
    99  	}
   100  
   101  	// Get the HTTP client.
   102  	client, err := r.Meta.Client()
   103  	if err != nil {
   104  		r.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   105  		return 1
   106  	}
   107  
   108  	// Validate the input flags. This is done by the HTTP API anyway, but there
   109  	// is no harm doing it here to avoid calls that we know wont succeed.
   110  	if group != "" && job == "" {
   111  		r.Ui.Error("Job flag must be supplied when using group flag")
   112  		return 1
   113  
   114  	}
   115  	if task != "" && group == "" {
   116  		r.Ui.Error("Group flag must be supplied when using task flag")
   117  		return 1
   118  	}
   119  
   120  	// Setup the query params.
   121  	q := &api.QueryOptions{
   122  		Params: map[string]string{},
   123  	}
   124  	if job != "" {
   125  		q.Params["job"] = job
   126  	}
   127  	if group != "" {
   128  		q.Params["group"] = group
   129  	}
   130  	if task != "" {
   131  		q.Params["task"] = task
   132  	}
   133  
   134  	recommendations, _, err := client.Recommendations().List(q)
   135  	if err != nil {
   136  		r.Ui.Error(fmt.Sprintf("Error listing recommendations: %s", err))
   137  		return 1
   138  	}
   139  
   140  	if len(recommendations) == 0 {
   141  		r.Ui.Output("No recommendations found")
   142  		return 0
   143  	}
   144  
   145  	if json || len(tmpl) > 0 {
   146  		out, err := Format(json, tmpl, recommendations)
   147  		if err != nil {
   148  			r.Ui.Error(err.Error())
   149  			return 1
   150  		}
   151  		r.Ui.Output(out)
   152  		return 0
   153  	}
   154  
   155  	// Create the output table header.
   156  	output := []string{"ID|"}
   157  
   158  	// If the operator is using the namespace wildcard option, add this header.
   159  	if r.Meta.namespace == "*" {
   160  		output[0] += "Namespace|"
   161  	}
   162  	output[0] += "Job|Group|Task|Resource|Value"
   163  
   164  	// Sort the list of recommendations based on their job, group and task.
   165  	sortedRecs := recommendationList{r: recommendations}
   166  	sort.Sort(sortedRecs)
   167  
   168  	// Iterate the recommendations and add to the output.
   169  	for i, rec := range sortedRecs.r {
   170  
   171  		output = append(output, rec.ID)
   172  
   173  		if r.Meta.namespace == "*" {
   174  			output[i+1] += fmt.Sprintf("|%s", rec.Namespace)
   175  		}
   176  		output[i+1] += fmt.Sprintf("|%s|%s|%s|%s|%v", rec.JobID, rec.Group, rec.Task, rec.Resource, rec.Value)
   177  	}
   178  
   179  	// Output.
   180  	r.Ui.Output(formatList(output))
   181  	return 0
   182  }
   183  
   184  // recommendationList is a wrapper around []*api.Recommendation that lets us
   185  // sort the recommendations alphabetically based on their job, group and task.
   186  type recommendationList struct {
   187  	r []*api.Recommendation
   188  }
   189  
   190  // Len satisfies the Len function of the sort.Interface interface.
   191  func (r recommendationList) Len() int { return len(r.r) }
   192  
   193  // Swap satisfies the Swap function of the sort.Interface interface.
   194  func (r recommendationList) Swap(i, j int) {
   195  	r.r[i], r.r[j] = r.r[j], r.r[i]
   196  }
   197  
   198  // Less satisfies the Less function of the sort.Interface interface.
   199  func (r recommendationList) Less(i, j int) bool {
   200  	recI := r.stringFromResource(i)
   201  	recJ := r.stringFromResource(j)
   202  	stringList := []string{recI, recJ}
   203  	sort.Strings(stringList)
   204  	return stringList[0] == recI
   205  }
   206  
   207  func (r recommendationList) stringFromResource(i int) string {
   208  	return strings.Join([]string{r.r[i].Namespace, r.r[i].JobID, r.r[i].Group, r.r[i].Task, r.r[i].Resource}, ":")
   209  }