github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/engine/job.go (about)

     1  package engine
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"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.
    21  type Job struct {
    22  	Eng     *Engine
    23  	Name    string
    24  	Args    []string
    25  	env     *Env
    26  	Stdout  *Output
    27  	Stderr  *Output
    28  	Stdin   *Input
    29  	handler Handler
    30  	end     time.Time
    31  	closeIO bool
    32  
    33  	// When closed, the job has been cancelled.
    34  	// Note: not all jobs implement cancellation.
    35  	// See Job.Cancel() and Job.WaitCancelled()
    36  	cancelled  chan struct{}
    37  	cancelOnce sync.Once
    38  }
    39  
    40  // Run executes the job and blocks until the job completes.
    41  // If the job fails it returns an error
    42  func (job *Job) Run() (err error) {
    43  	defer func() {
    44  		// Wait for all background tasks to complete
    45  		if job.closeIO {
    46  			if err := job.Stdout.Close(); err != nil {
    47  				logrus.Error(err)
    48  			}
    49  			if err := job.Stderr.Close(); err != nil {
    50  				logrus.Error(err)
    51  			}
    52  			if err := job.Stdin.Close(); err != nil {
    53  				logrus.Error(err)
    54  			}
    55  		}
    56  	}()
    57  
    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  		logrus.Infof("+job %s", job.CallString())
    80  		defer func() {
    81  			okerr := "OK"
    82  			if err != nil {
    83  				okerr = fmt.Sprintf("ERR: %s", err)
    84  			}
    85  			logrus.Infof("-job %s %s", job.CallString(), okerr)
    86  		}()
    87  	}
    88  
    89  	if job.handler == nil {
    90  		return fmt.Errorf("%s: command not found", job.Name)
    91  	}
    92  
    93  	var errorMessage = bytes.NewBuffer(nil)
    94  	job.Stderr.Add(errorMessage)
    95  
    96  	err = job.handler(job)
    97  	job.end = time.Now()
    98  
    99  	return
   100  }
   101  
   102  func (job *Job) CallString() string {
   103  	return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", "))
   104  }
   105  
   106  func (job *Job) Env() *Env {
   107  	return job.env
   108  }
   109  
   110  func (job *Job) EnvExists(key string) (value bool) {
   111  	return job.env.Exists(key)
   112  }
   113  
   114  func (job *Job) Getenv(key string) (value string) {
   115  	return job.env.Get(key)
   116  }
   117  
   118  func (job *Job) GetenvBool(key string) (value bool) {
   119  	return job.env.GetBool(key)
   120  }
   121  
   122  func (job *Job) SetenvBool(key string, value bool) {
   123  	job.env.SetBool(key, value)
   124  }
   125  
   126  func (job *Job) GetenvTime(key string) (value time.Time, err error) {
   127  	return job.env.GetTime(key)
   128  }
   129  
   130  func (job *Job) SetenvTime(key string, value time.Time) {
   131  	job.env.SetTime(key, value)
   132  }
   133  
   134  func (job *Job) GetenvSubEnv(key string) *Env {
   135  	return job.env.GetSubEnv(key)
   136  }
   137  
   138  func (job *Job) SetenvSubEnv(key string, value *Env) error {
   139  	return job.env.SetSubEnv(key, value)
   140  }
   141  
   142  func (job *Job) GetenvInt64(key string) int64 {
   143  	return job.env.GetInt64(key)
   144  }
   145  
   146  func (job *Job) GetenvInt(key string) int {
   147  	return job.env.GetInt(key)
   148  }
   149  
   150  func (job *Job) SetenvInt64(key string, value int64) {
   151  	job.env.SetInt64(key, value)
   152  }
   153  
   154  func (job *Job) SetenvInt(key string, value int) {
   155  	job.env.SetInt(key, value)
   156  }
   157  
   158  // Returns nil if key not found
   159  func (job *Job) GetenvList(key string) []string {
   160  	return job.env.GetList(key)
   161  }
   162  
   163  func (job *Job) GetenvJson(key string, iface interface{}) error {
   164  	return job.env.GetJson(key, iface)
   165  }
   166  
   167  func (job *Job) SetenvJson(key string, value interface{}) error {
   168  	return job.env.SetJson(key, value)
   169  }
   170  
   171  func (job *Job) SetenvList(key string, value []string) error {
   172  	return job.env.SetJson(key, value)
   173  }
   174  
   175  func (job *Job) Setenv(key, value string) {
   176  	job.env.Set(key, value)
   177  }
   178  
   179  // DecodeEnv decodes `src` as a json dictionary, and adds
   180  // each decoded key-value pair to the environment.
   181  //
   182  // If `src` cannot be decoded as a json dictionary, an error
   183  // is returned.
   184  func (job *Job) DecodeEnv(src io.Reader) error {
   185  	return job.env.Decode(src)
   186  }
   187  
   188  func (job *Job) EncodeEnv(dst io.Writer) error {
   189  	return job.env.Encode(dst)
   190  }
   191  
   192  func (job *Job) ImportEnv(src interface{}) (err error) {
   193  	return job.env.Import(src)
   194  }
   195  
   196  func (job *Job) Environ() map[string]string {
   197  	return job.env.Map()
   198  }
   199  
   200  func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
   201  	return fmt.Fprintf(job.Stdout, format, args...)
   202  }
   203  
   204  func (job *Job) Errorf(format string, args ...interface{}) (n int, err error) {
   205  	return fmt.Fprintf(job.Stderr, format, args...)
   206  }
   207  
   208  func (job *Job) SetCloseIO(val bool) {
   209  	job.closeIO = val
   210  }
   211  
   212  // When called, causes the Job.WaitCancelled channel to unblock.
   213  func (job *Job) Cancel() {
   214  	job.cancelOnce.Do(func() {
   215  		close(job.cancelled)
   216  	})
   217  }
   218  
   219  // Returns a channel which is closed ("never blocks") when the job is cancelled.
   220  func (job *Job) WaitCancelled() <-chan struct{} {
   221  	return job.cancelled
   222  }