This allows clients an easier way to interact with the fifos for a container without having to use the built in copyIO functions when opening fifos. It's nothing that clients could not have already coded but since we use this type of functionality in the tests it makes sense to add an implementation here. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
		
			
				
	
	
		
			173 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// +build !windows
 | 
						|
 | 
						|
package containerd
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"sync"
 | 
						|
	"syscall"
 | 
						|
 | 
						|
	"github.com/containerd/fifo"
 | 
						|
)
 | 
						|
 | 
						|
// NewFifos returns a new set of fifos for the task
 | 
						|
func NewFifos(id string) (*FIFOSet, error) {
 | 
						|
	root := filepath.Join(os.TempDir(), "containerd")
 | 
						|
	if err := os.MkdirAll(root, 0700); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	dir, err := ioutil.TempDir(root, "")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &FIFOSet{
 | 
						|
		Dir: dir,
 | 
						|
		In:  filepath.Join(dir, id+"-stdin"),
 | 
						|
		Out: filepath.Join(dir, id+"-stdout"),
 | 
						|
		Err: filepath.Join(dir, id+"-stderr"),
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func copyIO(fifos *FIFOSet, ioset *ioSet, tty bool) (_ *wgCloser, err error) {
 | 
						|
	var (
 | 
						|
		f           io.ReadWriteCloser
 | 
						|
		set         []io.Closer
 | 
						|
		ctx, cancel = context.WithCancel(context.Background())
 | 
						|
		wg          = &sync.WaitGroup{}
 | 
						|
	)
 | 
						|
	defer func() {
 | 
						|
		if err != nil {
 | 
						|
			for _, f := range set {
 | 
						|
				f.Close()
 | 
						|
			}
 | 
						|
			cancel()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	if f, err = fifo.OpenFifo(ctx, fifos.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	set = append(set, f)
 | 
						|
	go func(w io.WriteCloser) {
 | 
						|
		io.Copy(w, ioset.in)
 | 
						|
		w.Close()
 | 
						|
	}(f)
 | 
						|
 | 
						|
	if f, err = fifo.OpenFifo(ctx, fifos.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	set = append(set, f)
 | 
						|
	wg.Add(1)
 | 
						|
	go func(r io.ReadCloser) {
 | 
						|
		io.Copy(ioset.out, r)
 | 
						|
		r.Close()
 | 
						|
		wg.Done()
 | 
						|
	}(f)
 | 
						|
 | 
						|
	if f, err = fifo.OpenFifo(ctx, fifos.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	set = append(set, f)
 | 
						|
 | 
						|
	if !tty {
 | 
						|
		wg.Add(1)
 | 
						|
		go func(r io.ReadCloser) {
 | 
						|
			io.Copy(ioset.err, r)
 | 
						|
			r.Close()
 | 
						|
			wg.Done()
 | 
						|
		}(f)
 | 
						|
	}
 | 
						|
	return &wgCloser{
 | 
						|
		wg:     wg,
 | 
						|
		dir:    fifos.Dir,
 | 
						|
		set:    set,
 | 
						|
		cancel: cancel,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewDirectIO returns an IO implementation that exposes the pipes directly
 | 
						|
func NewDirectIO(ctx context.Context, terminal bool) (*DirectIO, error) {
 | 
						|
	set, err := NewFifos("")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	f := &DirectIO{
 | 
						|
		set:      set,
 | 
						|
		terminal: terminal,
 | 
						|
	}
 | 
						|
	defer func() {
 | 
						|
		if err != nil {
 | 
						|
			f.Delete()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
	if f.Stdin, err = fifo.OpenFifo(ctx, set.In, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if f.Stdout, err = fifo.OpenFifo(ctx, set.Out, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		f.Stdin.Close()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if f.Stderr, err = fifo.OpenFifo(ctx, set.Err, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil {
 | 
						|
		f.Stdin.Close()
 | 
						|
		f.Stdout.Close()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return f, nil
 | 
						|
}
 | 
						|
 | 
						|
type DirectIO struct {
 | 
						|
	Stdin  io.WriteCloser
 | 
						|
	Stdout io.ReadCloser
 | 
						|
	Stderr io.ReadCloser
 | 
						|
 | 
						|
	set      *FIFOSet
 | 
						|
	terminal bool
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) IOCreate(id string) (IO, error) {
 | 
						|
	return f, nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) IOAttach(set *FIFOSet) (IO, error) {
 | 
						|
	return f, nil
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) Config() IOConfig {
 | 
						|
	return IOConfig{
 | 
						|
		Terminal: f.terminal,
 | 
						|
		Stdin:    f.set.In,
 | 
						|
		Stdout:   f.set.Out,
 | 
						|
		Stderr:   f.set.Err,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) Cancel() {
 | 
						|
	// nothing to cancel as all operations are handled externally
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) Wait() {
 | 
						|
	// nothing to wait on as all operations are handled externally
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) Close() error {
 | 
						|
	err := f.Stdin.Close()
 | 
						|
	if err2 := f.Stdout.Close(); err == nil {
 | 
						|
		err = err2
 | 
						|
	}
 | 
						|
	if err2 := f.Stderr.Close(); err == nil {
 | 
						|
		err = err2
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (f *DirectIO) Delete() error {
 | 
						|
	if f.set.Dir == "" {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return os.RemoveAll(f.set.Dir)
 | 
						|
}
 |