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

     1  package engine
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"sync"
     9  	"unicode"
    10  )
    11  
    12  type Output struct {
    13  	sync.Mutex
    14  	dests []io.Writer
    15  	tasks sync.WaitGroup
    16  	used  bool
    17  }
    18  
    19  // Tail returns the n last lines of a buffer
    20  // stripped out of trailing white spaces, if any.
    21  //
    22  // if n <= 0, returns an empty string
    23  func Tail(buffer *bytes.Buffer, n int) string {
    24  	if n <= 0 {
    25  		return ""
    26  	}
    27  	s := strings.TrimRightFunc(buffer.String(), unicode.IsSpace)
    28  	i := len(s) - 1
    29  	for ; i >= 0 && n > 0; i-- {
    30  		if s[i] == '\n' {
    31  			n--
    32  			if n == 0 {
    33  				break
    34  			}
    35  		}
    36  	}
    37  	// when i == -1, return the whole string which is s[0:]
    38  	return s[i+1:]
    39  }
    40  
    41  // NewOutput returns a new Output object with no destinations attached.
    42  // Writing to an empty Output will cause the written data to be discarded.
    43  func NewOutput() *Output {
    44  	return &Output{}
    45  }
    46  
    47  // Return true if something was written on this output
    48  func (o *Output) Used() bool {
    49  	o.Lock()
    50  	defer o.Unlock()
    51  	return o.used
    52  }
    53  
    54  // Add attaches a new destination to the Output. Any data subsequently written
    55  // to the output will be written to the new destination in addition to all the others.
    56  // This method is thread-safe.
    57  func (o *Output) Add(dst io.Writer) {
    58  	o.Lock()
    59  	defer o.Unlock()
    60  	o.dests = append(o.dests, dst)
    61  }
    62  
    63  // Set closes and remove existing destination and then attaches a new destination to
    64  // the Output. Any data subsequently written to the output will be written to the new
    65  // destination in addition to all the others. This method is thread-safe.
    66  func (o *Output) Set(dst io.Writer) {
    67  	o.Close()
    68  	o.Lock()
    69  	defer o.Unlock()
    70  	o.dests = []io.Writer{dst}
    71  }
    72  
    73  // AddPipe creates an in-memory pipe with io.Pipe(), adds its writing end as a destination,
    74  // and returns its reading end for consumption by the caller.
    75  // This is a rough equivalent similar to Cmd.StdoutPipe() in the standard os/exec package.
    76  // This method is thread-safe.
    77  func (o *Output) AddPipe() (io.Reader, error) {
    78  	r, w := io.Pipe()
    79  	o.Add(w)
    80  	return r, nil
    81  }
    82  
    83  // Write writes the same data to all registered destinations.
    84  // This method is thread-safe.
    85  func (o *Output) Write(p []byte) (n int, err error) {
    86  	o.Lock()
    87  	defer o.Unlock()
    88  	o.used = true
    89  	var firstErr error
    90  	for _, dst := range o.dests {
    91  		_, err := dst.Write(p)
    92  		if err != nil && firstErr == nil {
    93  			firstErr = err
    94  		}
    95  	}
    96  	return len(p), firstErr
    97  }
    98  
    99  // Close unregisters all destinations and waits for all background
   100  // AddTail and AddString tasks to complete.
   101  // The Close method of each destination is called if it exists.
   102  func (o *Output) Close() error {
   103  	o.Lock()
   104  	defer o.Unlock()
   105  	var firstErr error
   106  	for _, dst := range o.dests {
   107  		if closer, ok := dst.(io.Closer); ok {
   108  			err := closer.Close()
   109  			if err != nil && firstErr == nil {
   110  				firstErr = err
   111  			}
   112  		}
   113  	}
   114  	o.tasks.Wait()
   115  	o.dests = nil
   116  	return firstErr
   117  }
   118  
   119  type Input struct {
   120  	src io.Reader
   121  	sync.Mutex
   122  }
   123  
   124  // NewInput returns a new Input object with no source attached.
   125  // Reading to an empty Input will return io.EOF.
   126  func NewInput() *Input {
   127  	return &Input{}
   128  }
   129  
   130  // Read reads from the input in a thread-safe way.
   131  func (i *Input) Read(p []byte) (n int, err error) {
   132  	i.Mutex.Lock()
   133  	defer i.Mutex.Unlock()
   134  	if i.src == nil {
   135  		return 0, io.EOF
   136  	}
   137  	return i.src.Read(p)
   138  }
   139  
   140  // Closes the src
   141  // Not thread safe on purpose
   142  func (i *Input) Close() error {
   143  	if i.src != nil {
   144  		if closer, ok := i.src.(io.Closer); ok {
   145  			return closer.Close()
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  // Add attaches a new source to the input.
   152  // Add can only be called once per input. Subsequent calls will
   153  // return an error.
   154  func (i *Input) Add(src io.Reader) error {
   155  	i.Mutex.Lock()
   156  	defer i.Mutex.Unlock()
   157  	if i.src != nil {
   158  		return fmt.Errorf("Maximum number of sources reached: 1")
   159  	}
   160  	i.src = src
   161  	return nil
   162  }
   163  
   164  // AddEnv starts a new goroutine which will decode all subsequent data
   165  // as a stream of json-encoded objects, and point `dst` to the last
   166  // decoded object.
   167  // The result `env` can be queried using the type-neutral Env interface.
   168  // It is not safe to query `env` until the Output is closed.
   169  func (o *Output) AddEnv() (dst *Env, err error) {
   170  	src, err := o.AddPipe()
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	dst = &Env{}
   175  	o.tasks.Add(1)
   176  	go func() {
   177  		defer o.tasks.Done()
   178  		decoder := NewDecoder(src)
   179  		for {
   180  			env, err := decoder.Decode()
   181  			if err != nil {
   182  				return
   183  			}
   184  			*dst = *env
   185  		}
   186  	}()
   187  	return dst, nil
   188  }