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