github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/command/run.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/nomad/api"
    10  	"github.com/hashicorp/nomad/jobspec"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  type RunCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *RunCommand) Help() string {
    19  	helpText := `
    20  Usage: nomad run [options] <file>
    21  
    22    Starts running a new job or updates an existing job using
    23    the specification located at <file>. This is the main command
    24    used to interact with Nomad.
    25  
    26    Upon successful job submission, this command will immediately
    27    enter an interactive monitor. This is useful to watch Nomad's
    28    internals make scheduling decisions and place the submitted work
    29    onto nodes. The monitor will end once job placement is done. It
    30    is safe to exit the monitor early using ctrl+c.
    31  
    32    On successful job submission and scheduling, exit code 0 will be
    33    returned. If there are job placement issues encountered
    34    (unsatisfiable constraints, resource exhaustion, etc), then the
    35    exit code will be 2. Any other errors, including client connection
    36    issues or internal errors, are indicated by exit code 1.
    37  
    38  General Options:
    39  
    40    ` + generalOptionsUsage() + `
    41  
    42  Run Options:
    43  
    44    -detach
    45      Return immediately instead of entering monitor mode. After job
    46      submission, the evaluation ID will be printed to the screen.
    47      You can use this ID to start a monitor using the eval-monitor
    48      command later if needed.
    49  `
    50  	return strings.TrimSpace(helpText)
    51  }
    52  
    53  func (c *RunCommand) Synopsis() string {
    54  	return "Run a new job or update an existing job"
    55  }
    56  
    57  func (c *RunCommand) Run(args []string) int {
    58  	var detach bool
    59  
    60  	flags := c.Meta.FlagSet("run", FlagSetClient)
    61  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    62  	flags.BoolVar(&detach, "detach", false, "")
    63  
    64  	if err := flags.Parse(args); err != nil {
    65  		return 1
    66  	}
    67  
    68  	// Check that we got exactly one node
    69  	args = flags.Args()
    70  	if len(args) != 1 {
    71  		c.Ui.Error(c.Help())
    72  		return 1
    73  	}
    74  	file := args[0]
    75  
    76  	// Parse the job file
    77  	job, err := jobspec.ParseFile(file)
    78  	if err != nil {
    79  		c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %s", file, err))
    80  		return 1
    81  	}
    82  
    83  	// Check that the job is valid
    84  	if err := job.Validate(); err != nil {
    85  		c.Ui.Error(fmt.Sprintf("Error validating job: %s", err))
    86  		return 1
    87  	}
    88  
    89  	// Convert it to something we can use
    90  	apiJob, err := convertJob(job)
    91  	if err != nil {
    92  		c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
    93  		return 1
    94  	}
    95  
    96  	// Get the HTTP client
    97  	client, err := c.Meta.Client()
    98  	if err != nil {
    99  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
   100  		return 1
   101  	}
   102  
   103  	// Submit the job
   104  	evalID, _, err := client.Jobs().Register(apiJob, nil)
   105  	if err != nil {
   106  		c.Ui.Error(fmt.Sprintf("Error submitting job: %s", err))
   107  		return 1
   108  	}
   109  
   110  	// Check if we should enter monitor mode
   111  	if detach {
   112  		c.Ui.Output("Job registration successful")
   113  		c.Ui.Output("Evaluation ID: " + evalID)
   114  		return 0
   115  	}
   116  
   117  	// Detach was not specified, so start monitoring
   118  	mon := newMonitor(c.Ui, client)
   119  	return mon.monitor(evalID)
   120  
   121  }
   122  
   123  // convertJob is used to take a *structs.Job and convert it to an *api.Job.
   124  // This function is just a hammer and probably needs to be revisited.
   125  func convertJob(in *structs.Job) (*api.Job, error) {
   126  	var apiJob *api.Job
   127  	buf := new(bytes.Buffer)
   128  	if err := gob.NewEncoder(buf).Encode(in); err != nil {
   129  		return nil, err
   130  	}
   131  	if err := gob.NewDecoder(buf).Decode(&apiJob); err != nil {
   132  		return nil, err
   133  	}
   134  	return apiJob, nil
   135  }