2017-11-21 08:08:36 +00:00
|
|
|
package runcexecutor
|
2017-06-01 22:24:23 +00:00
|
|
|
|
|
|
|
import (
|
2018-01-16 22:30:10 +00:00
|
|
|
"context"
|
2017-06-01 22:24:23 +00:00
|
|
|
"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"
|
2018-05-30 02:49:43 +00:00
|
|
|
"strings"
|
2020-07-10 22:05:30 +00:00
|
|
|
"sync"
|
2017-06-01 22:24:23 +00:00
|
|
|
"syscall"
|
2018-09-19 22:50:46 +00:00
|
|
|
"time"
|
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"
|
2018-02-09 19:39:48 +00:00
|
|
|
"github.com/containerd/continuity/fs"
|
2017-06-01 22:24:23 +00:00
|
|
|
runc "github.com/containerd/go-runc"
|
2019-03-20 21:54:20 +00:00
|
|
|
"github.com/docker/docker/pkg/idtools"
|
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"
|
2020-08-05 01:11:32 +00:00
|
|
|
"github.com/moby/buildkit/solver/errdefs"
|
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-07-04 07:13:30 +00:00
|
|
|
rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
|
2020-04-23 01:30:19 +00:00
|
|
|
"github.com/moby/buildkit/util/stack"
|
2020-07-09 23:07:28 +00:00
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
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
|
|
|
)
|
|
|
|
|
2018-04-17 23:37:45 +00:00
|
|
|
type Opt struct {
|
2018-05-30 02:49:43 +00:00
|
|
|
// root directory
|
2018-04-17 23:37:45 +00:00
|
|
|
Root string
|
|
|
|
CommandCandidates []string
|
2018-05-30 02:49:43 +00:00
|
|
|
// without root privileges (has nothing to do with Opt.Root directory)
|
|
|
|
Rootless bool
|
2018-08-19 06:05:26 +00:00
|
|
|
// DefaultCgroupParent is the cgroup-parent name for executor
|
|
|
|
DefaultCgroupParent string
|
2019-01-03 10:12:12 +00:00
|
|
|
// ProcessMode
|
2019-03-20 21:54:20 +00:00
|
|
|
ProcessMode oci.ProcessMode
|
|
|
|
IdentityMapping *idtools.IdentityMapping
|
2019-05-07 14:22:55 +00:00
|
|
|
// runc run --no-pivot (unrecommended)
|
2019-08-16 05:53:54 +00:00
|
|
|
NoPivot bool
|
|
|
|
DNS *oci.DNSConfig
|
|
|
|
OOMScoreAdj *int
|
2018-04-17 23:37:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
type runcExecutor struct {
|
2018-08-21 15:51:28 +00:00
|
|
|
runc *runc.Runc
|
|
|
|
root string
|
2018-08-19 06:05:26 +00:00
|
|
|
cgroupParent string
|
2018-08-21 15:51:28 +00:00
|
|
|
rootless bool
|
|
|
|
networkProviders map[pb.NetMode]network.Provider
|
2019-01-03 10:12:12 +00:00
|
|
|
processMode oci.ProcessMode
|
2019-03-20 21:54:20 +00:00
|
|
|
idmap *idtools.IdentityMapping
|
2019-05-07 14:22:55 +00:00
|
|
|
noPivot bool
|
2019-06-06 01:46:52 +00:00
|
|
|
dns *oci.DNSConfig
|
2019-08-16 05:53:54 +00:00
|
|
|
oomScoreAdj *int
|
2020-07-10 22:05:30 +00:00
|
|
|
running map[string]chan error
|
|
|
|
mu sync.Mutex
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2018-08-21 15:51:28 +00:00
|
|
|
func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
|
2018-04-17 23:37:45 +00:00
|
|
|
cmds := opt.CommandCandidates
|
|
|
|
if cmds == nil {
|
|
|
|
cmds = defaultCommandCandidates
|
2017-06-22 20:42:13 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 23:37:45 +00:00
|
|
|
var cmd string
|
|
|
|
var found bool
|
|
|
|
for _, cmd = range cmds {
|
|
|
|
if _, err := exec.LookPath(cmd); err == nil {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
return nil, errors.Errorf("failed to find %s binary", cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
root := opt.Root
|
|
|
|
|
2019-06-10 23:34:09 +00:00
|
|
|
if err := os.MkdirAll(root, 0711); err != nil {
|
2017-06-09 01:16:19 +00:00
|
|
|
return nil, errors.Wrapf(err, "failed to create %s", root)
|
|
|
|
}
|
|
|
|
|
|
|
|
root, err := filepath.Abs(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-07 07:04:55 +00:00
|
|
|
root, err = filepath.EvalSymlinks(root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-06-09 01:16:19 +00:00
|
|
|
|
2018-10-03 20:59:33 +00:00
|
|
|
// clean up old hosts/resolv.conf file. ignore errors
|
|
|
|
os.RemoveAll(filepath.Join(root, "hosts"))
|
|
|
|
os.RemoveAll(filepath.Join(root, "resolv.conf"))
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
runtime := &runc.Runc{
|
2018-04-19 00:01:20 +00:00
|
|
|
Command: cmd,
|
2017-06-01 22:24:23 +00:00
|
|
|
Log: filepath.Join(root, "runc-log.json"),
|
|
|
|
LogFormat: runc.JSON,
|
2018-09-19 22:50:46 +00:00
|
|
|
PdeathSignal: syscall.SIGKILL, // this can still leak the process
|
2017-07-03 04:21:40 +00:00
|
|
|
Setpgid: true,
|
2018-07-04 07:13:30 +00:00
|
|
|
// we don't execute runc with --rootless=(true|false) explicitly,
|
|
|
|
// so as to support non-runc runtimes
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2017-11-21 08:08:36 +00:00
|
|
|
w := &runcExecutor{
|
2018-08-21 15:51:28 +00:00
|
|
|
runc: runtime,
|
|
|
|
root: root,
|
2018-08-19 06:05:26 +00:00
|
|
|
cgroupParent: opt.DefaultCgroupParent,
|
2018-08-21 15:51:28 +00:00
|
|
|
rootless: opt.Rootless,
|
|
|
|
networkProviders: networkProviders,
|
2019-01-03 10:12:12 +00:00
|
|
|
processMode: opt.ProcessMode,
|
2019-03-20 21:54:20 +00:00
|
|
|
idmap: opt.IdentityMapping,
|
2019-05-07 14:22:55 +00:00
|
|
|
noPivot: opt.NoPivot,
|
2019-06-06 01:46:52 +00:00
|
|
|
dns: opt.DNS,
|
2019-08-16 05:53:54 +00:00
|
|
|
oomScoreAdj: opt.OOMScoreAdj,
|
2020-07-10 22:05:30 +00:00
|
|
|
running: make(map[string]chan error),
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
return w, nil
|
|
|
|
}
|
|
|
|
|
2020-07-11 00:02:09 +00:00
|
|
|
func (w *runcExecutor) Run(ctx context.Context, id string, root cache.Mountable, mounts []executor.Mount, process executor.ProcessInfo, started chan<- struct{}) (err error) {
|
2020-07-09 23:07:28 +00:00
|
|
|
meta := process.Meta
|
2020-07-10 22:05:30 +00:00
|
|
|
|
|
|
|
startedOnce := sync.Once{}
|
|
|
|
done := make(chan error, 1)
|
|
|
|
w.mu.Lock()
|
|
|
|
w.running[id] = done
|
|
|
|
w.mu.Unlock()
|
|
|
|
defer func() {
|
|
|
|
w.mu.Lock()
|
|
|
|
delete(w.running, id)
|
|
|
|
w.mu.Unlock()
|
2020-07-11 00:02:09 +00:00
|
|
|
done <- err
|
2020-07-10 22:05:30 +00:00
|
|
|
close(done)
|
|
|
|
if started != nil {
|
|
|
|
startedOnce.Do(func() {
|
|
|
|
close(started)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-08-21 15:51:28 +00:00
|
|
|
provider, ok := w.networkProviders[meta.NetMode]
|
|
|
|
if !ok {
|
2020-07-11 00:02:09 +00:00
|
|
|
return errors.Errorf("unknown network mode %s", meta.NetMode)
|
2018-08-21 15:51:28 +00:00
|
|
|
}
|
|
|
|
namespace, err := provider.New()
|
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
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-08-03 17:00:54 +00:00
|
|
|
logrus.Info("enabling HostNetworking")
|
|
|
|
}
|
2017-08-08 01:45:31 +00:00
|
|
|
|
2019-06-06 01:46:52 +00:00
|
|
|
resolvConf, err := oci.GetResolvConf(ctx, w.root, w.idmap, w.dns)
|
2017-12-11 02:18:18 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-12-11 02:18:18 +00:00
|
|
|
}
|
|
|
|
|
2019-06-10 23:35:03 +00:00
|
|
|
hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts, w.idmap)
|
2017-12-11 02:18:18 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-12-11 02:18:18 +00:00
|
|
|
}
|
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-06-01 22:24:23 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 22:42:03 +00:00
|
|
|
rootMount, release, err := mountable.Mount()
|
2018-04-16 22:23:10 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2018-04-16 22:23:10 +00:00
|
|
|
}
|
2019-08-15 22:42:03 +00:00
|
|
|
if release != nil {
|
|
|
|
defer release()
|
|
|
|
}
|
2018-04-16 22:23:10 +00:00
|
|
|
|
2020-07-09 23:07:28 +00:00
|
|
|
if id == "" {
|
|
|
|
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
|
|
|
|
2019-06-10 23:34:09 +00:00
|
|
|
if err := os.Mkdir(bundle, 0711); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
defer os.RemoveAll(bundle)
|
2019-03-20 21:54:20 +00:00
|
|
|
|
|
|
|
identity := idtools.Identity{}
|
|
|
|
if w.idmap != nil {
|
|
|
|
identity = w.idmap.RootPair()
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
rootFSPath := filepath.Join(bundle, "rootfs")
|
2019-03-20 21:54:20 +00:00
|
|
|
if err := idtools.MkdirAllAndChown(rootFSPath, 0700, identity); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
2017-12-11 21:17:07 +00:00
|
|
|
if err := mount.All(rootMount, rootFSPath); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-12-11 21:17:07 +00:00
|
|
|
}
|
|
|
|
defer mount.Unmount(rootFSPath, 0)
|
|
|
|
|
2020-07-30 09:31:03 +00:00
|
|
|
uid, gid, sgids, err := oci.GetUser(rootFSPath, meta.User)
|
2017-12-11 21:17:07 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-12-11 21:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
f, err := os.Create(filepath.Join(bundle, "config.json"))
|
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
defer f.Close()
|
2018-03-16 02:51:32 +00:00
|
|
|
|
2018-06-29 23:00:24 +00:00
|
|
|
opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
|
2019-01-10 02:24:25 +00:00
|
|
|
|
2018-03-26 13:51:08 +00:00
|
|
|
if meta.ReadonlyRootFS {
|
|
|
|
opts = append(opts, containerdoci.WithRootFSReadonly())
|
|
|
|
}
|
2018-08-19 06:05:26 +00:00
|
|
|
|
2019-05-12 05:11:26 +00:00
|
|
|
identity = idtools.Identity{
|
|
|
|
UID: int(uid),
|
|
|
|
GID: int(gid),
|
|
|
|
}
|
|
|
|
if w.idmap != nil {
|
|
|
|
identity, err = w.idmap.ToHost(identity)
|
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2019-05-12 05:11:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
}
|
2019-03-20 21:54:20 +00:00
|
|
|
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, w.idmap, opts...)
|
2017-06-01 22:24:23 +00:00
|
|
|
if err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
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 {
|
2020-07-11 00:02:09 +00:00
|
|
|
return errors.Wrapf(err, "working dir %s points to invalid target", newp)
|
2017-07-07 21:35:10 +00:00
|
|
|
}
|
2019-07-26 23:08:49 +00:00
|
|
|
if _, err := os.Stat(newp); err != nil {
|
|
|
|
if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return errors.Wrapf(err, "failed to create working directory %s", newp)
|
2019-07-26 23:08:49 +00:00
|
|
|
}
|
2017-07-07 21:35:10 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 23:44:42 +00:00
|
|
|
if meta.Tty {
|
|
|
|
return errors.New("tty with runc not implemented")
|
|
|
|
}
|
|
|
|
|
2019-08-16 05:53:54 +00:00
|
|
|
spec.Process.OOMScoreAdj = w.oomScoreAdj
|
2018-05-30 02:49:43 +00:00
|
|
|
if w.rootless {
|
2018-07-04 07:13:30 +00:00
|
|
|
if err := rootlessspecconv.ToRootless(spec); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2018-05-30 02:49:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-01 22:24:23 +00:00
|
|
|
if err := json.NewEncoder(f).Encode(spec); err != nil {
|
2020-07-11 00:02:09 +00:00
|
|
|
return err
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 22:50:46 +00:00
|
|
|
// runCtx/killCtx is used for extra check in case the kill command blocks
|
|
|
|
runCtx, cancelRun := context.WithCancel(context.Background())
|
|
|
|
defer cancelRun()
|
|
|
|
|
2020-07-10 22:05:30 +00:00
|
|
|
ended := make(chan struct{})
|
2018-09-19 22:50:46 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
killCtx, timeout := context.WithTimeout(context.Background(), 7*time.Second)
|
|
|
|
if err := w.runc.Kill(killCtx, id, int(syscall.SIGKILL), nil); err != nil {
|
|
|
|
logrus.Errorf("failed to kill runc %s: %+v", id, err)
|
|
|
|
select {
|
|
|
|
case <-killCtx.Done():
|
|
|
|
timeout()
|
|
|
|
cancelRun()
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
timeout()
|
|
|
|
select {
|
|
|
|
case <-time.After(50 * time.Millisecond):
|
2020-07-10 22:05:30 +00:00
|
|
|
case <-ended:
|
2018-09-19 22:50:46 +00:00
|
|
|
return
|
|
|
|
}
|
2020-07-10 22:05:30 +00:00
|
|
|
case <-ended:
|
2018-09-19 22:50:46 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-03-16 02:51:32 +00:00
|
|
|
logrus.Debugf("> creating %s %v", id, meta.Args)
|
2020-07-10 22:05:30 +00:00
|
|
|
// this is a cheat, we have not actually started, but as close as we can get with runc for now
|
|
|
|
if started != nil {
|
|
|
|
startedOnce.Do(func() {
|
|
|
|
close(started)
|
|
|
|
})
|
|
|
|
}
|
2018-09-19 22:50:46 +00:00
|
|
|
status, err := w.runc.Run(runCtx, id, bundle, &runc.CreateOpts{
|
2020-07-09 23:07:28 +00:00
|
|
|
IO: &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr},
|
2019-05-07 14:22:55 +00:00
|
|
|
NoPivot: w.noPivot,
|
2018-08-03 17:00:54 +00:00
|
|
|
})
|
2020-07-10 22:05:30 +00:00
|
|
|
close(ended)
|
2018-03-16 02:51:32 +00:00
|
|
|
|
2019-09-11 17:57:40 +00:00
|
|
|
if status != 0 || err != nil {
|
2020-08-05 01:11:32 +00:00
|
|
|
exitErr := &errdefs.ExitError{
|
2020-07-30 23:44:42 +00:00
|
|
|
ExitCode: uint32(status),
|
|
|
|
Err: err,
|
2019-09-11 17:57:40 +00:00
|
|
|
}
|
2020-07-31 20:20:59 +00:00
|
|
|
err = exitErr
|
2018-09-26 22:52:31 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2020-07-31 20:20:59 +00:00
|
|
|
exitErr.Err = errors.Wrapf(ctx.Err(), exitErr.Error())
|
|
|
|
return exitErr
|
2018-09-26 22:52:31 +00:00
|
|
|
default:
|
2020-07-11 00:02:09 +00:00
|
|
|
return stack.Enable(err)
|
2018-09-26 22:52:31 +00:00
|
|
|
}
|
2018-03-16 02:51:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2017-06-01 22:24:23 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 23:44:42 +00:00
|
|
|
func (w *runcExecutor) Exec(ctx context.Context, id string, process executor.ProcessInfo) (err error) {
|
2020-07-09 23:07:28 +00:00
|
|
|
// first verify the container is running, if we get an error assume the container
|
|
|
|
// is in the process of being created and check again every 100ms or until
|
|
|
|
// context is canceled.
|
2020-07-11 00:02:09 +00:00
|
|
|
var state *runc.Container
|
2020-07-09 23:07:28 +00:00
|
|
|
for {
|
2020-07-11 00:02:09 +00:00
|
|
|
w.mu.Lock()
|
|
|
|
done, ok := w.running[id]
|
|
|
|
w.mu.Unlock()
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("container %s not found", id)
|
|
|
|
}
|
|
|
|
|
|
|
|
state, _ = w.runc.State(ctx, id)
|
2020-07-09 23:07:28 +00:00
|
|
|
if state != nil && state.Status == "running" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
2020-07-10 22:05:30 +00:00
|
|
|
case err, ok := <-done:
|
2020-07-11 00:02:09 +00:00
|
|
|
if !ok || err == nil {
|
2020-07-10 22:05:30 +00:00
|
|
|
return errors.Errorf("container %s has stopped", id)
|
|
|
|
}
|
|
|
|
return errors.Wrapf(err, "container %s has exited with error", id)
|
2020-07-09 23:07:28 +00:00
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// load default process spec (for Env, Cwd etc) from bundle
|
|
|
|
f, err := os.Open(filepath.Join(state.Bundle, "config.json"))
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
spec := &specs.Spec{}
|
|
|
|
if err := json.NewDecoder(f).Decode(spec); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if process.Meta.User != "" {
|
2020-07-30 09:31:03 +00:00
|
|
|
uid, gid, sgids, err := oci.GetUser(state.Rootfs, process.Meta.User)
|
2020-07-09 23:07:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
spec.Process.User = specs.User{
|
|
|
|
UID: uid,
|
|
|
|
GID: gid,
|
|
|
|
AdditionalGids: sgids,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spec.Process.Terminal = process.Meta.Tty
|
|
|
|
spec.Process.Args = process.Meta.Args
|
|
|
|
if process.Meta.Cwd != "" {
|
|
|
|
spec.Process.Cwd = process.Meta.Cwd
|
|
|
|
}
|
2020-07-10 22:05:30 +00:00
|
|
|
|
2020-07-09 23:07:28 +00:00
|
|
|
if len(process.Meta.Env) > 0 {
|
2020-07-10 22:05:30 +00:00
|
|
|
spec.Process.Env = process.Meta.Env
|
2020-07-09 23:07:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 23:44:42 +00:00
|
|
|
err = w.runc.Exec(ctx, id, *spec.Process, &runc.ExecOpts{
|
2020-07-09 23:07:28 +00:00
|
|
|
IO: &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr},
|
|
|
|
})
|
2020-07-30 23:44:42 +00:00
|
|
|
|
|
|
|
var exitError *exec.ExitError
|
|
|
|
if errors.As(err, &exitError) {
|
2020-08-05 01:11:32 +00:00
|
|
|
err = &errdefs.ExitError{
|
2020-07-30 23:44:42 +00:00
|
|
|
ExitCode: uint32(exitError.ExitCode()),
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2020-07-09 23:07:28 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 00:30:02 +00:00
|
|
|
type forwardIO struct {
|
2018-09-12 05:40:48 +00:00
|
|
|
stdin io.ReadCloser
|
|
|
|
stdout, stderr io.WriteCloser
|
2017-06-02 00:30:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *forwardIO) Close() error {
|
2018-08-21 15:51:28 +00:00
|
|
|
return nil
|
2017-06-02 00:30:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|