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