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 }