github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/container/stream/streams.go (about)

     1  package stream
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/containerd/containerd/cio"
    11  	"github.com/docker/docker/pkg/broadcaster"
    12  	"github.com/docker/docker/pkg/ioutils"
    13  	"github.com/docker/docker/pkg/pools"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  // Config holds information about I/O streams managed together.
    18  //
    19  // config.StdinPipe returns a WriteCloser which can be used to feed data
    20  // to the standard input of the streamConfig's active process.
    21  // config.StdoutPipe and streamConfig.StderrPipe each return a ReadCloser
    22  // which can be used to retrieve the standard output (and error) generated
    23  // by the container's active process. The output (and error) are actually
    24  // copied and delivered to all StdoutPipe and StderrPipe consumers, using
    25  // a kind of "broadcaster".
    26  type Config struct {
    27  	sync.WaitGroup
    28  	stdout    *broadcaster.Unbuffered
    29  	stderr    *broadcaster.Unbuffered
    30  	stdin     io.ReadCloser
    31  	stdinPipe io.WriteCloser
    32  }
    33  
    34  // NewConfig creates a stream config and initializes
    35  // the standard err and standard out to new unbuffered broadcasters.
    36  func NewConfig() *Config {
    37  	return &Config{
    38  		stderr: new(broadcaster.Unbuffered),
    39  		stdout: new(broadcaster.Unbuffered),
    40  	}
    41  }
    42  
    43  // Stdout returns the standard output in the configuration.
    44  func (c *Config) Stdout() *broadcaster.Unbuffered {
    45  	return c.stdout
    46  }
    47  
    48  // Stderr returns the standard error in the configuration.
    49  func (c *Config) Stderr() *broadcaster.Unbuffered {
    50  	return c.stderr
    51  }
    52  
    53  // Stdin returns the standard input in the configuration.
    54  func (c *Config) Stdin() io.ReadCloser {
    55  	return c.stdin
    56  }
    57  
    58  // StdinPipe returns an input writer pipe as an io.WriteCloser.
    59  func (c *Config) StdinPipe() io.WriteCloser {
    60  	return c.stdinPipe
    61  }
    62  
    63  // StdoutPipe creates a new io.ReadCloser with an empty bytes pipe.
    64  // It adds this new out pipe to the Stdout broadcaster.
    65  // This will block stdout if unconsumed.
    66  func (c *Config) StdoutPipe() io.ReadCloser {
    67  	bytesPipe := ioutils.NewBytesPipe()
    68  	c.stdout.Add(bytesPipe)
    69  	return bytesPipe
    70  }
    71  
    72  // StderrPipe creates a new io.ReadCloser with an empty bytes pipe.
    73  // It adds this new err pipe to the Stderr broadcaster.
    74  // This will block stderr if unconsumed.
    75  func (c *Config) StderrPipe() io.ReadCloser {
    76  	bytesPipe := ioutils.NewBytesPipe()
    77  	c.stderr.Add(bytesPipe)
    78  	return bytesPipe
    79  }
    80  
    81  // NewInputPipes creates new pipes for both standard inputs, Stdin and StdinPipe.
    82  func (c *Config) NewInputPipes() {
    83  	c.stdin, c.stdinPipe = io.Pipe()
    84  }
    85  
    86  // NewNopInputPipe creates a new input pipe that will silently drop all messages in the input.
    87  func (c *Config) NewNopInputPipe() {
    88  	c.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard)
    89  }
    90  
    91  // CloseStreams ensures that the configured streams are properly closed.
    92  func (c *Config) CloseStreams() error {
    93  	var errors []string
    94  
    95  	if c.stdin != nil {
    96  		if err := c.stdin.Close(); err != nil {
    97  			errors = append(errors, fmt.Sprintf("error close stdin: %s", err))
    98  		}
    99  	}
   100  
   101  	if err := c.stdout.Clean(); err != nil {
   102  		errors = append(errors, fmt.Sprintf("error close stdout: %s", err))
   103  	}
   104  
   105  	if err := c.stderr.Clean(); err != nil {
   106  		errors = append(errors, fmt.Sprintf("error close stderr: %s", err))
   107  	}
   108  
   109  	if len(errors) > 0 {
   110  		return fmt.Errorf(strings.Join(errors, "\n"))
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  // CopyToPipe connects streamconfig with a libcontainerd.IOPipe
   117  func (c *Config) CopyToPipe(iop *cio.DirectIO) {
   118  	copyFunc := func(w io.Writer, r io.ReadCloser) {
   119  		c.Add(1)
   120  		go func() {
   121  			if _, err := pools.Copy(w, r); err != nil {
   122  				logrus.Errorf("stream copy error: %v", err)
   123  			}
   124  			r.Close()
   125  			c.Done()
   126  		}()
   127  	}
   128  
   129  	if iop.Stdout != nil {
   130  		copyFunc(c.Stdout(), iop.Stdout)
   131  	}
   132  	if iop.Stderr != nil {
   133  		copyFunc(c.Stderr(), iop.Stderr)
   134  	}
   135  
   136  	if stdin := c.Stdin(); stdin != nil {
   137  		if iop.Stdin != nil {
   138  			go func() {
   139  				pools.Copy(iop.Stdin, stdin)
   140  				if err := iop.Stdin.Close(); err != nil {
   141  					logrus.Warnf("failed to close stdin: %v", err)
   142  				}
   143  			}()
   144  		}
   145  	}
   146  }