-
Notifications
You must be signed in to change notification settings - Fork 0
/
execer.go
112 lines (94 loc) · 3.15 KB
/
execer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package runrunc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"code.cloudfoundry.org/garden"
"code.cloudfoundry.org/idmapper"
"code.cloudfoundry.org/lager"
)
type Execer struct {
bundleLoader BundleLoader
processBuilder ProcessBuilder
mkdirer Mkdirer
userLookuper UserLookupper
runner ExecRunner
processIDGen UidGenerator
}
func NewExecer(bundleLoader BundleLoader, processBuilder ProcessBuilder, mkdirer Mkdirer, userLookuper UserLookupper, runner ExecRunner, processIDGen UidGenerator) *Execer {
return &Execer{
bundleLoader: bundleLoader,
processBuilder: processBuilder,
mkdirer: mkdirer,
userLookuper: userLookuper,
runner: runner,
processIDGen: processIDGen,
}
}
// Exec a process in a bundle using 'runc exec'
func (e *Execer) Exec(log lager.Logger, bundlePath, sandboxHandle string, spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
log = log.Session("exec", lager.Data{"id": sandboxHandle, "path": spec.Path})
log.Info("start")
defer log.Info("finished")
ctrInitPid, err := ioutil.ReadFile(filepath.Join(bundlePath, "pidfile"))
if err != nil {
log.Error("read-pidfile-failed", err)
return nil, err
}
rootfsPath := filepath.Join("/proc", string(ctrInitPid), "root")
user, err := e.userLookuper.Lookup(rootfsPath, spec.User)
if err != nil {
log.Error("user-lookup-failed", err)
return nil, err
}
bundle, err := e.bundleLoader.Load(bundlePath)
if err != nil {
log.Error("load-bundle-failed", err)
return nil, err
}
hostUID := idmapper.MappingList(bundle.Spec.Linux.UIDMappings).Map(user.Uid)
hostGID := idmapper.MappingList(bundle.Spec.Linux.GIDMappings).Map(user.Gid)
if spec.Dir == "" {
spec.Dir = user.Home
}
err = e.mkdirer.MkdirAs(rootfsPath, hostUID, hostGID, 0755, false, spec.Dir)
if err != nil {
log.Error("create-workdir-failed", err)
return nil, err
}
preparedSpec := e.processBuilder.BuildProcess(bundle, ProcessSpec{
ProcessSpec: spec,
ContainerUID: user.Uid,
ContainerGID: user.Gid,
})
processesPath := filepath.Join(bundlePath, "processes")
processID := spec.ID
if processID == "" {
processID = e.processIDGen.Generate()
}
processPath := filepath.Join(processesPath, processID)
if _, err := os.Stat(processPath); err == nil {
return nil, errors.New(fmt.Sprintf("process ID '%s' already in use", processID))
}
if err := os.MkdirAll(processPath, 0700); err != nil {
return nil, err
}
encodedSpec, err := json.Marshal(preparedSpec.Process)
if err != nil {
return nil, err // this could *almost* be a panic: a valid spec should always encode (but out of caution we'll error)
}
return e.runner.Run(
log, processID, processPath, sandboxHandle, bundlePath, preparedSpec.ContainerRootHostUID,
preparedSpec.ContainerRootHostGID, io, preparedSpec.Terminal, bytes.NewReader(encodedSpec), nil,
)
}
// Attach attaches to an already running process by guid
func (e *Execer) Attach(log lager.Logger, bundlePath, id, processID string, io garden.ProcessIO) (garden.Process, error) {
processesPath := path.Join(bundlePath, "processes")
return e.runner.Attach(log, processID, io, processesPath)
}