2017-11-21 08:08:36 +00:00
|
|
|
package runcexecutor
|
2017-06-01 22:24:23 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2017-06-02 00:30:02 +00:00
|
|
|
"io"
|
2017-06-01 22:24:23 +00:00
|
|
|
"os"
|
2017-06-02 00:30:02 +00:00
|
|
|
"os/exec"
|
2017-06-01 22:24:23 +00:00
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
|
2017-12-17 23:45:12 +00:00
|
|
|
"github.com/containerd/containerd/fs"
|
2017-06-01 22:24:23 +00:00
|
|
|
"github.com/containerd/containerd/mount"
|
2017-12-11 21:17:07 +00:00
|
|
|
containerdoci "github.com/containerd/containerd/oci"
|
2017-06-01 22:24:23 +00:00
|
|
|
runc "github.com/containerd/go-runc"
|
2017-06-22 20:15:46 +00:00
|
|
|
"github.com/moby/buildkit/cache"
|
2017-11-21 08:08:36 +00:00
|
|
|
"github.com/moby/buildkit/executor"
|
|
|
|
"github.com/moby/buildkit/executor/oci"
|
2017-10-03 23:13:35 +00:00
|
|
|
"github.com/moby/buildkit/identity"
|
2017-06-01 22:24:23 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-07-19 01:05:19 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-06-01 22:24:23 +00:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
type runcExecutor struct {
|
2017-06-01 22:24:23 +00:00
|
|
|
runc *runc.Runc
|
|
|
|
root string
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
func New(root string) (executor.Executor, error) {
|
2017-06-22 20:42:13 +00:00
|
|
|
if err := exec.Command("runc", "--version").Run(); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to find runc binary")
|
|
|
|
}
|
|
|
|
|
2017-06-09 01:16:19 +00:00
|
|
|
if err := os.MkdirAll(root, 0700); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to create %s", root)
|
|
|
|
}
|
|
|
|
|
|
|
|
root, err := filepath.Abs(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// TODO: check that root is not symlink to fail early
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
runtime := &runc.Runc{
|
|
|
|
Log: filepath.Join(root, "runc-log.json"),
|
|
|
|
LogFormat: runc.JSON,
|
|
|
|
PdeathSignal: syscall.SIGKILL,
|
2017-07-03 04:21:40 +00:00
|
|
|
Setpgid: true,
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
w := &runcExecutor{
|
2017-06-01 22:24:23 +00:00
|
|
|
runc: runtime,
|
|
|
|
root: root,
|
|
|
|
}
|
|
|
|
return w, nil
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
|
2017-08-08 01:45:31 +00:00
|
|
|
|
2017-12-11 02:18:18 +00:00
|
|
|
resolvConf, err := oci.GetResolvConf(ctx, w.root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
hostsFile, err := oci.GetHostsFile(ctx, w.root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-18 06:08:22 +00:00
|
|
|
rootMount, err := root.Mount(ctx, false)
|
2017-06-01 22:24:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-03 23:13:35 +00:00
|
|
|
id := identity.NewID()
|
2017-06-01 22:24:23 +00:00
|
|
|
bundle := filepath.Join(w.root, id)
|
2017-06-09 01:16:19 +00:00
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
if err := os.Mkdir(bundle, 0700); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(bundle)
|
|
|
|
rootFSPath := filepath.Join(bundle, "rootfs")
|
|
|
|
if err := os.Mkdir(rootFSPath, 0700); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-11 21:17:07 +00:00
|
|
|
if err := mount.All(rootMount, rootFSPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer mount.Unmount(rootFSPath, 0)
|
|
|
|
|
|
|
|
uid, gid, err := oci.GetUser(ctx, rootFSPath, meta.User)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
f, err := os.Create(filepath.Join(bundle, "config.json"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2017-12-11 21:17:07 +00:00
|
|
|
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, containerdoci.WithUIDGID(uid, gid))
|
2017-06-01 22:24:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-08 01:45:31 +00:00
|
|
|
defer cleanup()
|
2017-06-01 22:24:23 +00:00
|
|
|
|
|
|
|
spec.Root.Path = rootFSPath
|
2017-07-07 21:35:10 +00:00
|
|
|
if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type
|
2017-06-01 22:24:23 +00:00
|
|
|
spec.Root.Readonly = true
|
|
|
|
}
|
|
|
|
|
2017-12-17 23:45:12 +00:00
|
|
|
newp, err := fs.RootPath(rootFSPath, meta.Cwd)
|
2017-07-07 21:35:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "working dir %s points to invalid target", newp)
|
|
|
|
}
|
|
|
|
if err := os.MkdirAll(newp, 0700); err != nil {
|
|
|
|
return errors.Wrapf(err, "failed to create working directory %s", newp)
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
if err := json.NewEncoder(f).Encode(spec); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-09 01:16:19 +00:00
|
|
|
logrus.Debugf("> running %s %v", id, meta.Args)
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
status, err := w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
|
2017-10-01 00:58:07 +00:00
|
|
|
IO: &forwardIO{stdin: stdin, stdout: stdout, stderr: stderr},
|
2017-06-01 22:24:23 +00:00
|
|
|
})
|
2017-06-09 01:16:19 +00:00
|
|
|
logrus.Debugf("< completed %s %v %v", id, status, err)
|
2017-06-01 22:24:23 +00:00
|
|
|
if status != 0 {
|
2017-06-29 22:31:08 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
// runc can't report context.Cancelled directly
|
|
|
|
return errors.Wrapf(ctx.Err(), "exit code %d", status)
|
|
|
|
default:
|
|
|
|
}
|
2017-06-01 22:24:23 +00:00
|
|
|
return errors.Errorf("exit code %d", status)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-02 00:30:02 +00:00
|
|
|
type forwardIO struct {
|
2017-10-01 00:58:07 +00:00
|
|
|
stdin io.ReadCloser
|
2017-06-02 00:30:02 +00:00
|
|
|
stdout, stderr io.WriteCloser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Set(cmd *exec.Cmd) {
|
2017-10-01 00:58:07 +00:00
|
|
|
cmd.Stdin = s.stdin
|
2017-06-02 00:30:02 +00:00
|
|
|
cmd.Stdout = s.stdout
|
|
|
|
cmd.Stderr = s.stderr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Stdin() io.WriteCloser {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Stdout() io.ReadCloser {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Stderr() io.ReadCloser {
|
|
|
|
return nil
|
|
|
|
}
|