8040df4e89
* Vendor in runc afaa21f79ade3b2e99a68f3f15e7219155aa4662 This updates the Dockerfile to use go 1.6.2 and install pkg-config are both are now needed by runc. Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Add support for runc create/start operation Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Remove dependency on runc state directory for OOM handler Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com> * Add OOM test Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
174 lines
3.7 KiB
Go
174 lines
3.7 KiB
Go
package runtime
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/docker/containerd/specs"
|
|
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
|
)
|
|
|
|
func findCgroupMountpointAndRoot(pid int, subsystem string) (string, string, error) {
|
|
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
txt := scanner.Text()
|
|
fields := strings.Split(txt, " ")
|
|
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
|
if opt == subsystem {
|
|
return fields[4], fields[3], nil
|
|
}
|
|
}
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
return "", "", fmt.Errorf("cgroup path for %s not found", subsystem)
|
|
}
|
|
|
|
func parseCgroupFile(path string) (map[string]string, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
s := bufio.NewScanner(f)
|
|
cgroups := make(map[string]string)
|
|
|
|
for s.Scan() {
|
|
if err := s.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
text := s.Text()
|
|
parts := strings.Split(text, ":")
|
|
|
|
for _, subs := range strings.Split(parts[1], ",") {
|
|
cgroups[subs] = parts[2]
|
|
}
|
|
}
|
|
return cgroups, nil
|
|
}
|
|
|
|
func (c *container) OOM() (OOM, error) {
|
|
p := c.processes[InitProcessID]
|
|
if p == nil {
|
|
return nil, fmt.Errorf("no init process found")
|
|
}
|
|
|
|
mountpoint, hostRoot, err := findCgroupMountpointAndRoot(os.Getpid(), "memory")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cgroups, err := parseCgroupFile(fmt.Sprintf("/proc/%d/cgroup", p.pid))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
root, ok := cgroups["memory"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("no memory cgroup for container %s", c.ID())
|
|
}
|
|
|
|
// Take care of the case were we're running inside a container
|
|
// ourself
|
|
root = strings.TrimPrefix(root, hostRoot)
|
|
|
|
return c.getMemeoryEventFD(filepath.Join(mountpoint, root))
|
|
}
|
|
|
|
func u64Ptr(i uint64) *uint64 { return &i }
|
|
|
|
func (c *container) UpdateResources(r *Resource) error {
|
|
sr := ocs.Resources{
|
|
Memory: &ocs.Memory{
|
|
Limit: u64Ptr(uint64(r.Memory)),
|
|
Reservation: u64Ptr(uint64(r.MemoryReservation)),
|
|
Swap: u64Ptr(uint64(r.MemorySwap)),
|
|
Kernel: u64Ptr(uint64(r.KernelMemory)),
|
|
KernelTCP: u64Ptr(uint64(r.KernelTCPMemory)),
|
|
},
|
|
CPU: &ocs.CPU{
|
|
Shares: u64Ptr(uint64(r.CPUShares)),
|
|
Quota: u64Ptr(uint64(r.CPUQuota)),
|
|
Period: u64Ptr(uint64(r.CPUPeriod)),
|
|
Cpus: &r.CpusetCpus,
|
|
Mems: &r.CpusetMems,
|
|
},
|
|
BlockIO: &ocs.BlockIO{
|
|
Weight: &r.BlkioWeight,
|
|
},
|
|
}
|
|
|
|
srStr := bytes.NewBuffer(nil)
|
|
if err := json.NewEncoder(srStr).Encode(&sr); err != nil {
|
|
return err
|
|
}
|
|
|
|
args := c.runtimeArgs
|
|
args = append(args, "update", "-r", "-", c.id)
|
|
cmd := exec.Command(c.runtime, args...)
|
|
cmd.Stdin = srStr
|
|
b, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf(string(b))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getRootIDs(s *specs.Spec) (int, int, error) {
|
|
if s == nil {
|
|
return 0, 0, nil
|
|
}
|
|
var hasUserns bool
|
|
for _, ns := range s.Linux.Namespaces {
|
|
if ns.Type == ocs.UserNamespace {
|
|
hasUserns = true
|
|
break
|
|
}
|
|
}
|
|
if !hasUserns {
|
|
return 0, 0, nil
|
|
}
|
|
uid := hostIDFromMap(0, s.Linux.UIDMappings)
|
|
gid := hostIDFromMap(0, s.Linux.GIDMappings)
|
|
return uid, gid, nil
|
|
}
|
|
|
|
func (c *container) getMemeoryEventFD(root string) (*oom, error) {
|
|
f, err := os.Open(filepath.Join(root, "memory.oom_control"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
|
|
if serr != 0 {
|
|
f.Close()
|
|
return nil, serr
|
|
}
|
|
if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
|
|
syscall.Close(int(fd))
|
|
f.Close()
|
|
return nil, err
|
|
}
|
|
return &oom{
|
|
root: root,
|
|
id: c.id,
|
|
eventfd: int(fd),
|
|
control: f,
|
|
}, nil
|
|
}
|