github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/engine/streams.go (about)

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