github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/command/run.go (about)

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