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 }