2017-11-21 08:08:36 +00:00
|
|
|
package containerdexecutor
|
2017-07-26 00:23:11 +00:00
|
|
|
|
|
|
|
import (
|
2018-01-16 22:30:10 +00:00
|
|
|
"context"
|
2017-07-26 00:23:11 +00:00
|
|
|
"io"
|
2018-08-19 06:05:26 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2017-12-10 01:17:20 +00:00
|
|
|
"syscall"
|
|
|
|
"time"
|
2017-07-26 00:23:11 +00:00
|
|
|
|
|
|
|
"github.com/containerd/containerd"
|
2017-11-17 21:34:42 +00:00
|
|
|
"github.com/containerd/containerd/cio"
|
2018-05-24 23:34:35 +00:00
|
|
|
"github.com/containerd/containerd/contrib/seccomp"
|
2017-12-11 21:17:07 +00:00
|
|
|
containerdoci "github.com/containerd/containerd/oci"
|
2017-07-26 00:23:11 +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-07-26 00:23:11 +00:00
|
|
|
"github.com/moby/buildkit/identity"
|
2017-12-11 21:17:07 +00:00
|
|
|
"github.com/moby/buildkit/snapshot"
|
2018-08-04 19:42:01 +00:00
|
|
|
"github.com/moby/buildkit/solver/pb"
|
2018-03-16 02:51:32 +00:00
|
|
|
"github.com/moby/buildkit/util/network"
|
2018-05-24 23:34:35 +00:00
|
|
|
"github.com/moby/buildkit/util/system"
|
2017-07-26 00:23:11 +00:00
|
|
|
"github.com/pkg/errors"
|
2018-03-16 02:51:32 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-07-26 00:23:11 +00:00
|
|
|
)
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
type containerdExecutor struct {
|
2018-08-21 15:51:28 +00:00
|
|
|
client *containerd.Client
|
|
|
|
root string
|
|
|
|
networkProviders map[pb.NetMode]network.Provider
|
2018-08-19 06:05:26 +00:00
|
|
|
cgroupParent string
|
2017-07-26 00:23:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 17:49:25 +00:00
|
|
|
// New creates a new executor backed by connection to containerd API
|
2018-08-19 06:05:26 +00:00
|
|
|
func New(client *containerd.Client, root, cgroup string, networkProviders map[pb.NetMode]network.Provider) executor.Executor {
|
2017-11-21 08:08:36 +00:00
|
|
|
return containerdExecutor{
|
2018-08-21 15:51:28 +00:00
|
|
|
client: client,
|
|
|
|
root: root,
|
|
|
|
networkProviders: networkProviders,
|
2018-08-19 06:05:26 +00:00
|
|
|
cgroupParent: cgroup,
|
2017-07-26 00:23:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
func (w containerdExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) (err error) {
|
2017-07-26 00:23:11 +00:00
|
|
|
id := identity.NewID()
|
|
|
|
|
2017-12-11 02:18:18 +00:00
|
|
|
resolvConf, err := oci.GetResolvConf(ctx, w.root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-03 21:01:54 +00:00
|
|
|
hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts)
|
2017-12-11 02:18:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-03 21:01:54 +00:00
|
|
|
if clean != nil {
|
|
|
|
defer clean()
|
2018-08-01 21:15:43 +00:00
|
|
|
}
|
2017-12-11 02:18:18 +00:00
|
|
|
|
2018-04-16 22:23:10 +00:00
|
|
|
mountable, err := root.Mount(ctx, false)
|
2017-07-26 00:23:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-16 22:23:10 +00:00
|
|
|
rootMounts, err := mountable.Mount()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer mountable.Release()
|
|
|
|
|
2018-06-29 23:00:24 +00:00
|
|
|
var sgids []uint32
|
|
|
|
uid, gid, err := oci.ParseUIDGID(meta.User)
|
2017-12-11 21:17:07 +00:00
|
|
|
if err != nil {
|
2018-04-16 22:23:10 +00:00
|
|
|
lm := snapshot.LocalMounterWithMounts(rootMounts)
|
2017-12-11 21:17:07 +00:00
|
|
|
rootfsPath, err := lm.Mount()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-29 23:00:24 +00:00
|
|
|
uid, gid, sgids, err = oci.GetUser(ctx, rootfsPath, meta.User)
|
2017-12-11 21:17:07 +00:00
|
|
|
if err != nil {
|
|
|
|
lm.Unmount()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
lm.Unmount()
|
|
|
|
}
|
|
|
|
|
2018-08-21 15:51:28 +00:00
|
|
|
provider, ok := w.networkProviders[meta.NetMode]
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("unknown network mode %s", meta.NetMode)
|
|
|
|
}
|
|
|
|
namespace, err := provider.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-08-03 17:00:54 +00:00
|
|
|
}
|
2018-08-21 15:51:28 +00:00
|
|
|
defer namespace.Close()
|
|
|
|
|
2018-08-04 19:42:01 +00:00
|
|
|
if meta.NetMode == pb.NetMode_HOST {
|
2018-03-16 02:51:32 +00:00
|
|
|
logrus.Info("enabling HostNetworking")
|
|
|
|
}
|
|
|
|
|
2018-06-29 23:00:24 +00:00
|
|
|
opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
|
2018-03-26 13:51:08 +00:00
|
|
|
if meta.ReadonlyRootFS {
|
|
|
|
opts = append(opts, containerdoci.WithRootFSReadonly())
|
|
|
|
}
|
2018-05-24 23:34:35 +00:00
|
|
|
if system.SeccompSupported() {
|
|
|
|
opts = append(opts, seccomp.WithDefaultProfile())
|
|
|
|
}
|
2018-08-19 06:05:26 +00:00
|
|
|
if w.cgroupParent != "" {
|
|
|
|
var cgroupsPath string
|
|
|
|
lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
|
|
|
|
if strings.Contains(w.cgroupParent, ".slice") && lastSeparator == ":" {
|
|
|
|
cgroupsPath = w.cgroupParent + id
|
|
|
|
} else {
|
|
|
|
cgroupsPath = filepath.Join("/", w.cgroupParent, "buildkit", id)
|
|
|
|
}
|
|
|
|
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
|
|
|
|
}
|
2018-08-21 15:51:28 +00:00
|
|
|
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, opts...)
|
2017-07-26 00:23:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-11 21:17:07 +00:00
|
|
|
defer cleanup()
|
2017-07-26 00:23:11 +00:00
|
|
|
|
|
|
|
container, err := w.client.NewContainer(ctx, id,
|
|
|
|
containerd.WithSpec(spec),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-10 01:17:20 +00:00
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err1 := container.Delete(context.TODO()); err == nil && err1 != nil {
|
|
|
|
err = errors.Wrapf(err1, "failed to delete container %s", id)
|
|
|
|
}
|
|
|
|
}()
|
2017-07-26 00:23:11 +00:00
|
|
|
|
2018-01-12 01:49:09 +00:00
|
|
|
task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStreams(stdin, stdout, stderr)), containerd.WithRootFS(rootMounts))
|
2017-07-26 00:23:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-10 01:17:20 +00:00
|
|
|
defer func() {
|
|
|
|
if _, err1 := task.Delete(context.TODO()); err == nil && err1 != nil {
|
|
|
|
err = errors.Wrapf(err1, "failed to delete task %s", id)
|
|
|
|
}
|
|
|
|
}()
|
2017-07-26 00:23:11 +00:00
|
|
|
|
|
|
|
if err := task.Start(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-12-10 01:17:20 +00:00
|
|
|
statusCh, err := task.Wait(context.Background())
|
2017-07-26 00:23:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-10 01:17:20 +00:00
|
|
|
|
2017-12-13 01:20:39 +00:00
|
|
|
var cancel func()
|
2017-12-10 01:17:20 +00:00
|
|
|
ctxDone := ctx.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctxDone:
|
|
|
|
ctxDone = nil
|
2017-12-13 01:20:39 +00:00
|
|
|
var killCtx context.Context
|
|
|
|
killCtx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
|
2017-12-10 01:17:20 +00:00
|
|
|
task.Kill(killCtx, syscall.SIGKILL)
|
|
|
|
case status := <-statusCh:
|
2017-12-13 01:20:39 +00:00
|
|
|
if cancel != nil {
|
|
|
|
cancel()
|
|
|
|
}
|
2017-12-10 01:17:20 +00:00
|
|
|
if status.ExitCode() != 0 {
|
|
|
|
return errors.Errorf("process returned non-zero exit code: %d", status.ExitCode())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-07-26 00:23:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|