containerd/runtime/container_linux.go

135 lines
3 KiB
Go
Raw Normal View History

package runtime
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"github.com/docker/containerd/specs"
"github.com/opencontainers/runc/libcontainer"
ocs "github.com/opencontainers/runtime-spec/specs-go"
)
func (c *container) getLibctContainer() (libcontainer.Container, error) {
runtimeRoot := "/run/runc"
// Check that the root wasn't changed
for _, opt := range c.runtimeArgs {
if strings.HasPrefix(opt, "--root=") {
runtimeRoot = strings.TrimPrefix(opt, "--root=")
break
}
}
f, err := libcontainer.New(runtimeRoot, libcontainer.Cgroupfs)
if err != nil {
return nil, err
}
return f.Load(c.id)
}
func (c *container) OOM() (OOM, error) {
container, err := c.getLibctContainer()
if err != nil {
if lerr, ok := err.(libcontainer.Error); ok {
// with oom registration sometimes the container can run, exit, and be destroyed
// faster than we can get the state back so we can just ignore this
if lerr.Code() == libcontainer.ContainerNotExists {
return nil, ErrContainerExited
}
}
return nil, err
}
state, err := container.State()
if err != nil {
return nil, err
}
memoryPath := state.CgroupPaths["memory"]
return c.getMemeoryEventFD(memoryPath)
}
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)),
},
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
}