* 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>
		
			
				
	
	
		
			175 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 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
 | 
						|
}
 |