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 }