github.com/eatbyte/docker@v1.6.0/engine/job.go (about) 1 package engine 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strings" 8 "sync" 9 "time" 10 11 log "github.com/Sirupsen/logrus" 12 ) 13 14 // A job is the fundamental unit of work in the docker engine. 15 // Everything docker can do should eventually be exposed as a job. 16 // For example: execute a process in a container, create a new container, 17 // download an archive from the internet, serve the http api, etc. 18 // 19 // The job API is designed after unix processes: a job has a name, arguments, 20 // environment variables, standard streams for input, output and error, and 21 // an exit status which can indicate success (0) or error (anything else). 22 // 23 // For status, 0 indicates success, and any other integers indicates an error. 24 // This allows for richer error reporting. 25 // 26 type Job struct { 27 Eng *Engine 28 Name string 29 Args []string 30 env *Env 31 Stdout *Output 32 Stderr *Output 33 Stdin *Input 34 handler Handler 35 status Status 36 end time.Time 37 closeIO bool 38 39 // When closed, the job has been cancelled. 40 // Note: not all jobs implement cancellation. 41 // See Job.Cancel() and Job.WaitCancelled() 42 cancelled chan struct{} 43 cancelOnce sync.Once 44 } 45 46 type Status int 47 48 const ( 49 StatusOK Status = 0 50 StatusErr Status = 1 51 StatusNotFound Status = 127 52 ) 53 54 // Run executes the job and blocks until the job completes. 55 // If the job returns a failure status, an error is returned 56 // which includes the status. 57 func (job *Job) Run() error { 58 if job.Eng.IsShutdown() && !job.GetenvBool("overrideShutdown") { 59 return fmt.Errorf("engine is shutdown") 60 } 61 // FIXME: this is a temporary workaround to avoid Engine.Shutdown 62 // waiting 5 seconds for server/api.ServeApi to complete (which it never will) 63 // everytime the daemon is cleanly restarted. 64 // The permanent fix is to implement Job.Stop and Job.OnStop so that 65 // ServeApi can cooperate and terminate cleanly. 66 if job.Name != "serveapi" { 67 job.Eng.l.Lock() 68 job.Eng.tasks.Add(1) 69 job.Eng.l.Unlock() 70 defer job.Eng.tasks.Done() 71 } 72 // FIXME: make this thread-safe 73 // FIXME: implement wait 74 if !job.end.IsZero() { 75 return fmt.Errorf("%s: job has already completed", job.Name) 76 } 77 // Log beginning and end of the job 78 if job.Eng.Logging { 79 log.Infof("+job %s", job.CallString()) 80 defer func() { 81 log.Infof("-job %s%s", job.CallString(), job.StatusString()) 82 }() 83 } 84 var errorMessage = bytes.NewBuffer(nil) 85 job.Stderr.Add(errorMessage) 86 if job.handler == nil { 87 job.Errorf("%s: command not found", job.Name) 88 job.status = 127 89 } else { 90 job.status = job.handler(job) 91 job.end = time.Now() 92 } 93 if job.closeIO { 94 // Wait for all background tasks to complete 95 if err := job.Stdout.Close(); err != nil { 96 return err 97 } 98 if err := job.Stderr.Close(); err != nil { 99 return err 100 } 101 if err := job.Stdin.Close(); err != nil { 102 return err 103 } 104 } 105 if job.status != 0 { 106 return fmt.Errorf("%s", Tail(errorMessage, 1)) 107 } 108 109 return nil 110 } 111 112 func (job *Job) CallString() string { 113 return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", ")) 114 } 115 116 func (job *Job) StatusString() string { 117 // If the job hasn't completed, status string is empty 118 if job.end.IsZero() { 119 return "" 120 } 121 var okerr string 122 if job.status == StatusOK { 123 okerr = "OK" 124 } else { 125 okerr = "ERR" 126 } 127 return fmt.Sprintf(" = %s (%d)", okerr, job.status) 128 } 129 130 // String returns a human-readable description of `job` 131 func (job *Job) String() string { 132 return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString()) 133 } 134 135 func (job *Job) Env() *Env { 136 return job.env 137 } 138 139 func (job *Job) EnvExists(key string) (value bool) { 140 return job.env.Exists(key) 141 } 142 143 func (job *Job) Getenv(key string) (value string) { 144 return job.env.Get(key) 145 } 146 147 func (job *Job) GetenvBool(key string) (value bool) { 148 return job.env.GetBool(key) 149 } 150 151 func (job *Job) SetenvBool(key string, value bool) { 152 job.env.SetBool(key, value) 153 } 154 155 func (job *Job) GetenvTime(key string) (value time.Time, err error) { 156 return job.env.GetTime(key) 157 } 158 159 func (job *Job) SetenvTime(key string, value time.Time) { 160 job.env.SetTime(key, value) 161 } 162 163 func (job *Job) GetenvSubEnv(key string) *Env { 164 return job.env.GetSubEnv(key) 165 } 166 167 func (job *Job) SetenvSubEnv(key string, value *Env) error { 168 return job.env.SetSubEnv(key, value) 169 } 170 171 func (job *Job) GetenvInt64(key string) int64 { 172 return job.env.GetInt64(key) 173 } 174 175 func (job *Job) GetenvInt(key string) int { 176 return job.env.GetInt(key) 177 } 178 179 func (job *Job) SetenvInt64(key string, value int64) { 180 job.env.SetInt64(key, value) 181 } 182 183 func (job *Job) SetenvInt(key string, value int) { 184 job.env.SetInt(key, value) 185 } 186 187 // Returns nil if key not found 188 func (job *Job) GetenvList(key string) []string { 189 return job.env.GetList(key) 190 } 191 192 func (job *Job) GetenvJson(key string, iface interface{}) error { 193 return job.env.GetJson(key, iface) 194 } 195 196 func (job *Job) SetenvJson(key string, value interface{}) error { 197 return job.env.SetJson(key, value) 198 } 199 200 func (job *Job) SetenvList(key string, value []string) error { 201 return job.env.SetJson(key, value) 202 } 203 204 func (job *Job) Setenv(key, value string) { 205 job.env.Set(key, value) 206 } 207 208 // DecodeEnv decodes `src` as a json dictionary, and adds 209 // each decoded key-value pair to the environment. 210 // 211 // If `src` cannot be decoded as a json dictionary, an error 212 // is returned. 213 func (job *Job) DecodeEnv(src io.Reader) error { 214 return job.env.Decode(src) 215 } 216 217 func (job *Job) EncodeEnv(dst io.Writer) error { 218 return job.env.Encode(dst) 219 } 220 221 func (job *Job) ImportEnv(src interface{}) (err error) { 222 return job.env.Import(src) 223 } 224 225 func (job *Job) Environ() map[string]string { 226 return job.env.Map() 227 } 228 229 func (job *Job) Logf(format string, args ...interface{}) (n int, err error) { 230 prefixedFormat := fmt.Sprintf("[%s] %s\n", job, strings.TrimRight(format, "\n")) 231 return fmt.Fprintf(job.Stderr, prefixedFormat, args...) 232 } 233 234 func (job *Job) Printf(format string, args ...interface{}) (n int, err error) { 235 return fmt.Fprintf(job.Stdout, format, args...) 236 } 237 238 func (job *Job) Errorf(format string, args ...interface{}) Status { 239 if format[len(format)-1] != '\n' { 240 format = format + "\n" 241 } 242 fmt.Fprintf(job.Stderr, format, args...) 243 return StatusErr 244 } 245 246 func (job *Job) Error(err error) Status { 247 fmt.Fprintf(job.Stderr, "%s\n", err) 248 return StatusErr 249 } 250 251 func (job *Job) StatusCode() int { 252 return int(job.status) 253 } 254 255 func (job *Job) SetCloseIO(val bool) { 256 job.closeIO = val 257 } 258 259 // When called, causes the Job.WaitCancelled channel to unblock. 260 func (job *Job) Cancel() { 261 job.cancelOnce.Do(func() { 262 close(job.cancelled) 263 }) 264 } 265 266 // Returns a channel which is closed ("never blocks") when the job is cancelled. 267 func (job *Job) WaitCancelled() <-chan struct{} { 268 return job.cancelled 269 }