
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)
|
|
}
|