github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/command/job_dispatch.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/nomad/api/contexts"
    10  	flaghelper "github.com/hashicorp/nomad/helper/flag-helpers"
    11  	"github.com/posener/complete"
    12  )
    13  
    14  type JobDispatchCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *JobDispatchCommand) Help() string {
    19  	helpText := `
    20  Usage: nomad job dispatch [options] <parameterized job> [input source]
    21  
    22    Dispatch creates an instance of a parameterized job. A data payload to the
    23    dispatched instance can be provided via stdin by using "-" or by specifying a
    24    path to a file. Metadata can be supplied by using the meta flag one or more
    25    times.
    26  
    27    Upon successful creation, the dispatched job ID will be printed and the
    28    triggered evaluation will be monitored. This can be disabled by supplying the
    29    detach flag.
    30  
    31  General Options:
    32  
    33    ` + generalOptionsUsage() + `
    34  
    35  Dispatch Options:
    36  
    37    -meta <key>=<value>
    38      Meta takes a key/value pair separated by "=". The metadata key will be
    39      merged into the job's metadata. The job may define a default value for the
    40      key which is overridden when dispatching. The flag can be provided more than
    41      once to inject multiple metadata key/value pairs. Arbitrary keys are not
    42      allowed. The parameterized job must allow the key to be merged.
    43  
    44    -detach
    45      Return immediately instead of entering monitor mode. After job dispatch,
    46      the evaluation ID will be printed to the screen, which can be used to
    47      examine the evaluation using the eval-status command.
    48  
    49    -verbose
    50      Display full information.
    51  `
    52  	return strings.TrimSpace(helpText)
    53  }
    54  
    55  func (c *JobDispatchCommand) Synopsis() string {
    56  	return "Dispatch an instance of a parameterized job"
    57  }
    58  
    59  func (c *JobDispatchCommand) AutocompleteFlags() complete.Flags {
    60  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    61  		complete.Flags{
    62  			"-meta":    complete.PredictAnything,
    63  			"-detach":  complete.PredictNothing,
    64  			"-verbose": complete.PredictNothing,
    65  		})
    66  }
    67  
    68  func (c *JobDispatchCommand) AutocompleteArgs() complete.Predictor {
    69  	return complete.PredictFunc(func(a complete.Args) []string {
    70  		client, err := c.Meta.Client()
    71  		if err != nil {
    72  			return nil
    73  		}
    74  
    75  		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil)
    76  		if err != nil {
    77  			return []string{}
    78  		}
    79  		return resp.Matches[contexts.Jobs]
    80  	})
    81  }
    82  
    83  func (c *JobDispatchCommand) Name() string { return "job dispatch" }
    84  
    85  func (c *JobDispatchCommand) Run(args []string) int {
    86  	var detach, verbose bool
    87  	var meta []string
    88  
    89  	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
    90  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    91  	flags.BoolVar(&detach, "detach", false, "")
    92  	flags.BoolVar(&verbose, "verbose", false, "")
    93  	flags.Var((*flaghelper.StringFlag)(&meta), "meta", "")
    94  
    95  	if err := flags.Parse(args); err != nil {
    96  		return 1
    97  	}
    98  
    99  	// Truncate the id unless full length is requested
   100  	length := shortId
   101  	if verbose {
   102  		length = fullId
   103  	}
   104  
   105  	// Check that we got one or two arguments
   106  	args = flags.Args()
   107  	if l := len(args); l < 1 || l > 2 {
   108  		c.Ui.Error("This command takes one or two argument: <parameterized job> [input source]")
   109  		c.Ui.Error(commandErrorText(c))
   110  		return 1
   111  	}
   112  
   113  	job := args[0]
   114  	var payload []byte
   115  	var readErr error
   116  
   117  	// Read the input
   118  	if len(args) == 2 {
   119  		switch args[1] {
   120  		case "-":
   121  			payload, readErr = ioutil.ReadAll(os.Stdin)
   122  		default:
   123  			payload, readErr = ioutil.ReadFile(args[1])
   124  		}
   125  		if readErr != nil {
   126  			c.Ui.Error(fmt.Sprintf("Error reading input data: %v", readErr))
   127  			return 1
   128  		}
   129  	}
   130  
   131  	// Build the meta
   132  	metaMap := make(map[string]string, len(meta))
   133  	for _, m := range meta {
   134  		split := strings.SplitN(m, "=", 2)
   135  		if len(split) != 2 {
   136  			c.Ui.Error(fmt.Sprintf("Error parsing meta value: %v", m))
   137  			return 1
   138  		}
   139  
   140  		metaMap[split[0]] = split[1]
   141  	}
   142  
   143  	// Get the HTTP client
   144  	client, err := c.Meta.Client()
   145  	if err != nil {
   146  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   147  		return 1
   148  	}
   149  
   150  	// Dispatch the job
   151  	resp, _, err := client.Jobs().Dispatch(job, metaMap, payload, nil)
   152  	if err != nil {
   153  		c.Ui.Error(fmt.Sprintf("Failed to dispatch job: %s", err))
   154  		return 1
   155  	}
   156  
   157  	// See if an evaluation was created. If the job is periodic there will be no
   158  	// eval.
   159  	evalCreated := resp.EvalID != ""
   160  
   161  	basic := []string{
   162  		fmt.Sprintf("Dispatched Job ID|%s", resp.DispatchedJobID),
   163  	}
   164  	if evalCreated {
   165  		basic = append(basic, fmt.Sprintf("Evaluation ID|%s", limit(resp.EvalID, length)))
   166  	}
   167  	c.Ui.Output(formatKV(basic))
   168  
   169  	// Nothing to do
   170  	if detach || !evalCreated {
   171  		return 0
   172  	}
   173  
   174  	c.Ui.Output("")
   175  	mon := newMonitor(c.Ui, client, length)
   176  	return mon.monitor(resp.EvalID, false)
   177  }