github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/plugin/executor/containerd/containerd.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "io" 6 "path/filepath" 7 "sync" 8 9 "github.com/containerd/containerd/cio" 10 "github.com/containerd/containerd/linux/runctypes" 11 "github.com/docker/docker/api/errdefs" 12 "github.com/docker/docker/libcontainerd" 13 "github.com/opencontainers/runtime-spec/specs-go" 14 "github.com/pkg/errors" 15 "github.com/sirupsen/logrus" 16 ) 17 18 // PluginNamespace is the name used for the plugins namespace 19 var PluginNamespace = "plugins.moby" 20 21 // ExitHandler represents an object that is called when the exit event is received from containerd 22 type ExitHandler interface { 23 HandleExitEvent(id string) error 24 } 25 26 // New creates a new containerd plugin executor 27 func New(rootDir string, remote libcontainerd.Remote, exitHandler ExitHandler) (*Executor, error) { 28 e := &Executor{ 29 rootDir: rootDir, 30 exitHandler: exitHandler, 31 } 32 client, err := remote.NewClient(PluginNamespace, e) 33 if err != nil { 34 return nil, errors.Wrap(err, "error creating containerd exec client") 35 } 36 e.client = client 37 return e, nil 38 } 39 40 // Executor is the containerd client implementation of a plugin executor 41 type Executor struct { 42 rootDir string 43 client libcontainerd.Client 44 exitHandler ExitHandler 45 } 46 47 // Create creates a new container 48 func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error { 49 opts := runctypes.RuncOptions{ 50 RuntimeRoot: filepath.Join(e.rootDir, "runtime-root"), 51 } 52 ctx := context.Background() 53 err := e.client.Create(ctx, id, &spec, &opts) 54 if err != nil { 55 return err 56 } 57 58 _, err = e.client.Start(ctx, id, "", false, attachStreamsFunc(stdout, stderr)) 59 return err 60 } 61 62 // Restore restores a container 63 func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) error { 64 alive, _, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr)) 65 if err != nil && !errdefs.IsNotFound(err) { 66 return err 67 } 68 if !alive { 69 _, _, err = e.client.DeleteTask(context.Background(), id) 70 if err != nil && !errdefs.IsNotFound(err) { 71 logrus.WithError(err).Errorf("failed to delete container plugin %s task from containerd", id) 72 return err 73 } 74 75 err = e.client.Delete(context.Background(), id) 76 if err != nil && !errdefs.IsNotFound(err) { 77 logrus.WithError(err).Errorf("failed to delete container plugin %s from containerd", id) 78 return err 79 } 80 } 81 return nil 82 } 83 84 // IsRunning returns if the container with the given id is running 85 func (e *Executor) IsRunning(id string) (bool, error) { 86 status, err := e.client.Status(context.Background(), id) 87 return status == libcontainerd.StatusRunning, err 88 } 89 90 // Signal sends the specified signal to the container 91 func (e *Executor) Signal(id string, signal int) error { 92 return e.client.SignalProcess(context.Background(), id, libcontainerd.InitProcessName, signal) 93 } 94 95 // ProcessEvent handles events from containerd 96 // All events are ignored except the exit event, which is sent of to the stored handler 97 func (e *Executor) ProcessEvent(id string, et libcontainerd.EventType, ei libcontainerd.EventInfo) error { 98 switch et { 99 case libcontainerd.EventExit: 100 // delete task and container 101 if _, _, err := e.client.DeleteTask(context.Background(), id); err != nil { 102 logrus.WithError(err).Errorf("failed to delete container plugin %s task from containerd", id) 103 } 104 105 if err := e.client.Delete(context.Background(), id); err != nil { 106 logrus.WithError(err).Errorf("failed to delete container plugin %s from containerd", id) 107 } 108 return e.exitHandler.HandleExitEvent(ei.ContainerID) 109 } 110 return nil 111 } 112 113 type rio struct { 114 cio.IO 115 116 wg sync.WaitGroup 117 } 118 119 func (c *rio) Wait() { 120 c.wg.Wait() 121 c.IO.Wait() 122 } 123 124 func attachStreamsFunc(stdout, stderr io.WriteCloser) libcontainerd.StdioCallback { 125 return func(iop *libcontainerd.IOPipe) (cio.IO, error) { 126 if iop.Stdin != nil { 127 iop.Stdin.Close() 128 // closing stdin shouldn't be needed here, it should never be open 129 panic("plugin stdin shouldn't have been created!") 130 } 131 132 rio := &rio{IO: iop} 133 rio.wg.Add(2) 134 go func() { 135 io.Copy(stdout, iop.Stdout) 136 stdout.Close() 137 rio.wg.Done() 138 }() 139 go func() { 140 io.Copy(stderr, iop.Stderr) 141 stderr.Close() 142 rio.wg.Done() 143 }() 144 return rio, nil 145 } 146 }