1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Update libcontainer to 185328a42654f6dc9a41814e578

Mac address support to the netlink pkg.
Cgroup performance and memory issues.
Netlink refactoring.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2014-09-19 11:20:10 -07:00
parent 41f2b3c6bc
commit 2531fba389
11 changed files with 267 additions and 148 deletions

View file

@ -62,7 +62,7 @@ if [ "$1" = '--go' ]; then
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
fi
clone git github.com/docker/libcontainer 84ad9386a0240acb7475429a835d826007032bf9
clone git github.com/docker/libcontainer 185328a42654f6dc9a41814e57882f69d65f6ab7
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
rm -rf src/github.com/docker/libcontainer/vendor
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"

View file

@ -24,6 +24,23 @@ var (
CgroupProcesses = "cgroup.procs"
)
// The absolute path to the root of the cgroup hierarchies.
var cgroupRoot string
// TODO(vmarmol): Report error here, we'll probably need to wait for the new API.
func init() {
// we can pick any subsystem to find the root
cpuRoot, err := cgroups.FindCgroupMountpoint("cpu")
if err != nil {
return
}
cgroupRoot = filepath.Dir(cpuRoot)
if _, err := os.Stat(cgroupRoot); err != nil {
return
}
}
type subsystem interface {
// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
GetStats(path string, stats *cgroups.Stats) error
@ -121,15 +138,8 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
}
func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) {
// we can pick any subsystem to find the root
cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
if err != nil {
return nil, err
}
cgroupRoot = filepath.Dir(cgroupRoot)
if _, err := os.Stat(cgroupRoot); err != nil {
return nil, fmt.Errorf("cgroups fs not found")
if cgroupRoot == "" {
return nil, fmt.Errorf("failed to find the cgroup root")
}
cgroup := c.Name

View file

@ -14,58 +14,65 @@ type Container interface {
// Returns the current run state of the container.
//
// Errors: container no longer exists,
// system error.
RunState() (*RunState, error)
// Errors:
// ContainerDestroyed - Container no longer exists,
// SystemError - System error.
RunState() (*RunState, Error)
// Returns the current config of the container.
Config() *Config
// Start a process inside the container. Returns the PID of the new process (in the caller process's namespace) and a channel that will return the exit status of the process whenever it dies.
//
// Errors: container no longer exists,
// config is invalid,
// container is paused,
// system error.
Start(*ProcessConfig) (pid int, exitChan chan int, err error)
// Errors:
// ContainerDestroyed - Container no longer exists,
// ConfigInvalid - config is invalid,
// ContainerPaused - Container is paused,
// SystemError - System error.
Start(config *ProcessConfig) (pid int, exitChan chan int, err Error)
// Destroys the container after killing all running processes.
//
// Any event registrations are removed before the container is destroyed.
// No error is returned if the container is already destroyed.
//
// Errors: system error.
Destroy() error
// Errors:
// SystemError - System error.
Destroy() Error
// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
//
// Errors: container no longer exists,
// system error.
// Errors:
// ContainerDestroyed - Container no longer exists,
// SystemError - System error.
//
// Some of the returned PIDs may no longer refer to processes in the Container, unless
// the Container state is PAUSED in which case every PID in the slice is valid.
Processes() ([]int, error)
Processes() ([]int, Error)
// Returns statistics for the container.
//
// Errors: container no longer exists,
// system error.
Stats() (*ContainerStats, error)
// Errors:
// ContainerDestroyed - Container no longer exists,
// SystemError - System error.
Stats() (*ContainerStats, Error)
// If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses
// the execution of any user processes. Asynchronously, when the container finished being paused the
// state is changed to PAUSED.
// If the Container state is PAUSED, do nothing.
//
// Errors: container no longer exists,
// system error.
Pause() error
// Errors:
// ContainerDestroyed - Container no longer exists,
// SystemError - System error.
Pause() Error
// If the Container state is PAUSED, resumes the execution of any user processes in the
// Container before setting the Container state to RUNNING.
// If the Container state is RUNNING, do nothing.
//
// Errors: container no longer exists,
// system error.
Resume() error
// Errors:
// ContainerDestroyed - Container no longer exists,
// SystemError - System error.
Resume() Error
}

View file

@ -17,6 +17,12 @@ var (
ErrNotADeviceNode = errors.New("not a device node")
)
// Testing dependencies
var (
osLstat = os.Lstat
ioutilReadDir = ioutil.ReadDir
)
type Device struct {
Type rune `json:"type,omitempty"`
Path string `json:"path,omitempty"` // It is fine if this is an empty string in the case that you are using Wildcards
@ -42,7 +48,7 @@ func (device *Device) GetCgroupAllowString() string {
// Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct.
func GetDevice(path, cgroupPermissions string) (*Device, error) {
fileInfo, err := os.Lstat(path)
fileInfo, err := osLstat(path)
if err != nil {
return nil, err
}
@ -87,7 +93,7 @@ func GetHostDeviceNodes() ([]*Device, error) {
}
func getDeviceNodes(path string) ([]*Device, error) {
files, err := ioutil.ReadDir(path)
files, err := ioutilReadDir(path)
if err != nil {
return nil, err
}

View file

@ -0,0 +1,61 @@
package devices
import (
"errors"
"os"
"testing"
)
func TestGetDeviceLstatFailure(t *testing.T) {
testError := errors.New("test error")
// Override os.Lstat to inject error.
osLstat = func(path string) (os.FileInfo, error) {
return nil, testError
}
_, err := GetDevice("", "")
if err != testError {
t.Fatalf("Unexpected error %v, expected %v", err, testError)
}
}
func TestGetHostDeviceNodesIoutilReadDirFailure(t *testing.T) {
testError := errors.New("test error")
// Override ioutil.ReadDir to inject error.
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
return nil, testError
}
_, err := GetHostDeviceNodes()
if err != testError {
t.Fatalf("Unexpected error %v, expected %v", err, testError)
}
}
func TestGetHostDeviceNodesIoutilReadDirDeepFailure(t *testing.T) {
testError := errors.New("test error")
called := false
// Override ioutil.ReadDir to inject error after the first call.
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
if called {
return nil, testError
}
called = true
// Provoke a second call.
fi, err := os.Lstat("/tmp")
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
return []os.FileInfo{fi}, nil
}
_, err := GetHostDeviceNodes()
if err != testError {
t.Fatalf("Unexpected error %v, expected %v", err, testError)
}
}

View file

@ -0,0 +1,37 @@
package libcontainer
// API error code type.
type ErrorCode int
// API error codes.
const (
// Factory errors
IdInUse ErrorCode = iota
InvalidIdFormat
// TODO: add Load errors
// Container errors
ContainerDestroyed
ContainerPaused
// Common errors
ConfigInvalid
SystemError
)
// API Error type.
type Error interface {
error
// Returns the stack trace, if any, which identifies the
// point at which the error occurred.
Stack() []byte
// Returns a verbose string including the error message
// and a representation of the stack trace suitable for
// printing.
Detail() string
// Returns the error code for this error.
Code() ErrorCode
}

View file

@ -12,13 +12,13 @@ type Factory interface {
// Returns the new container with a running process.
//
// Errors:
// id is already in use by a container
// id has incorrect format
// config is invalid
// System error
// IdInUse - id is already in use by a container
// InvalidIdFormat - id has incorrect format
// ConfigInvalid - config is invalid
// SystemError - System error
//
// On error, any partially created container parts are cleaned up (the operation is atomic).
Create(id string, config *Config) (Container, error)
Create(id string, config *Config) (Container, Error)
// Load takes an ID for an existing container and reconstructs the container
// from the state.
@ -27,5 +27,6 @@ type Factory interface {
// Path does not exist
// Container is stopped
// System error
Load(id string) (Container, error)
// TODO: fix description
Load(id string) (Container, Error)
}

View file

@ -3,6 +3,8 @@ package netlink
import (
"encoding/binary"
"fmt"
"io"
"math/rand"
"net"
"sync/atomic"
"syscall"
@ -38,12 +40,15 @@ type ifreqFlags struct {
Ifruflags uint16
}
func nativeEndian() binary.ByteOrder {
var native binary.ByteOrder
func init() {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
return binary.BigEndian
native = binary.BigEndian
} else {
native = binary.LittleEndian
}
return binary.LittleEndian
}
func getIpFamily(ip net.IP) int {
@ -80,8 +85,6 @@ func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
}
func (msg *IfInfomsg) ToWireFormat() []byte {
native := nativeEndian()
length := syscall.SizeofIfInfomsg
b := make([]byte, length)
b[0] = msg.Family
@ -110,8 +113,6 @@ func newIfAddrmsg(family int) *IfAddrmsg {
}
func (msg *IfAddrmsg) ToWireFormat() []byte {
native := nativeEndian()
length := syscall.SizeofIfAddrmsg
b := make([]byte, length)
b[0] = msg.Family
@ -142,8 +143,6 @@ func newRtMsg() *RtMsg {
}
func (msg *RtMsg) ToWireFormat() []byte {
native := nativeEndian()
length := syscall.SizeofRtMsg
b := make([]byte, length)
b[0] = msg.Family
@ -202,8 +201,6 @@ func (a *RtAttr) Len() int {
}
func (a *RtAttr) ToWireFormat() []byte {
native := nativeEndian()
length := a.Len()
buf := make([]byte, rtaAlignOf(length))
@ -225,14 +222,18 @@ func (a *RtAttr) ToWireFormat() []byte {
return buf
}
func uint32Attr(t int, n uint32) *RtAttr {
buf := make([]byte, 4)
native.PutUint32(buf, n)
return newRtAttr(t, buf)
}
type NetlinkRequest struct {
syscall.NlMsghdr
Data []NetlinkRequestData
}
func (rr *NetlinkRequest) ToWireFormat() []byte {
native := nativeEndian()
length := rr.Len
dataBytes := make([][]byte, len(rr.Data))
for i, data := range rr.Data {
@ -329,36 +330,44 @@ func (s *NetlinkSocket) GetPid() (uint32, error) {
return 0, ErrWrongSockType
}
func (s *NetlinkSocket) HandleAck(seq uint32) error {
native := nativeEndian()
func (s *NetlinkSocket) CheckMessage(m syscall.NetlinkMessage, seq, pid uint32) error {
if m.Header.Seq != seq {
return fmt.Errorf("netlink: invalid seq %d, expected %d", m.Header.Seq, seq)
}
if m.Header.Pid != pid {
return fmt.Errorf("netlink: wrong pid %d, expected %d", m.Header.Pid, pid)
}
if m.Header.Type == syscall.NLMSG_DONE {
return io.EOF
}
if m.Header.Type == syscall.NLMSG_ERROR {
e := int32(native.Uint32(m.Data[0:4]))
if e == 0 {
return io.EOF
}
return syscall.Errno(-e)
}
return nil
}
func (s *NetlinkSocket) HandleAck(seq uint32) error {
pid, err := s.GetPid()
if err != nil {
return err
}
done:
outer:
for {
msgs, err := s.Receive()
if err != nil {
return err
}
for _, m := range msgs {
if m.Header.Seq != seq {
return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
}
if m.Header.Pid != pid {
return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
}
if m.Header.Type == syscall.NLMSG_DONE {
break done
}
if m.Header.Type == syscall.NLMSG_ERROR {
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
break done
if err := s.CheckMessage(m, seq, pid); err != nil {
if err == io.EOF {
break outer
}
return syscall.Errno(-error)
return err
}
}
}
@ -454,17 +463,11 @@ func AddRoute(destination, source, gateway, device string) error {
wb.AddData(attr)
}
var (
native = nativeEndian()
b = make([]byte, 4)
)
iface, err := net.InterfaceByName(device)
if err != nil {
return err
}
native.PutUint32(b, uint32(iface.Index))
wb.AddData(newRtAttr(syscall.RTA_OIF, b))
wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index)))
if err := s.Send(wb); err != nil {
return err
@ -538,15 +541,7 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error {
msg.Index = int32(iface.Index)
msg.Change = DEFAULT_CHANGE
wb.AddData(msg)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(mtu))
data := newRtAttr(syscall.IFLA_MTU, b)
wb.AddData(data)
wb.AddData(uint32Attr(syscall.IFLA_MTU, uint32(mtu)))
if err := s.Send(wb); err != nil {
return err
@ -570,15 +565,7 @@ func NetworkSetMaster(iface, master *net.Interface) error {
msg.Index = int32(iface.Index)
msg.Change = DEFAULT_CHANGE
wb.AddData(msg)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(master.Index))
data := newRtAttr(syscall.IFLA_MASTER, b)
wb.AddData(data)
wb.AddData(uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)))
if err := s.Send(wb); err != nil {
return err
@ -602,15 +589,7 @@ func NetworkSetNsPid(iface *net.Interface, nspid int) error {
msg.Index = int32(iface.Index)
msg.Change = DEFAULT_CHANGE
wb.AddData(msg)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(nspid))
data := newRtAttr(syscall.IFLA_NET_NS_PID, b)
wb.AddData(data)
wb.AddData(uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)))
if err := s.Send(wb); err != nil {
return err
@ -634,15 +613,7 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
msg.Index = int32(iface.Index)
msg.Change = DEFAULT_CHANGE
wb.AddData(msg)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(fd))
data := newRtAttr(IFLA_NET_NS_FD, b)
wb.AddData(data)
wb.AddData(uint32Attr(IFLA_NET_NS_FD, uint32(fd)))
if err := s.Send(wb); err != nil {
return err
@ -782,8 +753,6 @@ func NetworkLinkDel(name string) error {
// Returns an array of IPNet for all the currently routed subnets on ipv4
// This is similar to the first column of "ip route" output
func NetworkGetRoutes() ([]Route, error) {
native := nativeEndian()
s, err := getNetlinkSocket()
if err != nil {
return nil, err
@ -806,28 +775,18 @@ func NetworkGetRoutes() ([]Route, error) {
res := make([]Route, 0)
done:
outer:
for {
msgs, err := s.Receive()
if err != nil {
return nil, err
}
for _, m := range msgs {
if m.Header.Seq != wb.Seq {
return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
}
if m.Header.Pid != pid {
return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
}
if m.Header.Type == syscall.NLMSG_DONE {
break done
}
if m.Header.Type == syscall.NLMSG_ERROR {
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
break done
if err := s.CheckMessage(m, wb.Seq, pid); err != nil {
if err == io.EOF {
break outer
}
return nil, syscall.Errno(-error)
return nil, err
}
if m.Header.Type != syscall.RTM_NEWROUTE {
continue
@ -974,7 +933,7 @@ func CreateBridge(name string, setMacAddr bool) error {
return err
}
if setMacAddr {
return setBridgeMacAddress(s, name)
return NetworkSetMacAddress(name, randMacAddr())
}
return nil
}
@ -1030,22 +989,40 @@ func AddToBridge(iface, master *net.Interface) error {
return nil
}
func setBridgeMacAddress(s int, name string) error {
func randMacAddr() string {
hw := make(net.HardwareAddr, 6)
for i := 0; i < 6; i++ {
hw[i] = byte(rand.Intn(255))
}
hw[0] &^= 0x1 // clear multicast bit
hw[0] |= 0x2 // set local assignment bit (IEEE802)
return hw.String()
}
func NetworkSetMacAddress(name, addr string) error {
if len(name) >= IFNAMSIZ {
return fmt.Errorf("Interface name %s too long", name)
}
hw, err := net.ParseMAC(addr)
if err != nil {
return err
}
s, err := getIfSocket()
if err != nil {
return err
}
defer syscall.Close(s)
ifr := ifreqHwaddr{}
ifr.IfruHwaddr.Family = syscall.ARPHRD_ETHER
copy(ifr.IfrnName[:len(ifr.IfrnName)-1], name)
for i := 0; i < 6; i++ {
ifr.IfruHwaddr.Data[i] = randIfrDataByte()
ifr.IfruHwaddr.Data[i] = ifrDataByte(hw[i])
}
ifr.IfruHwaddr.Data[0] &^= 0x1 // clear multicast bit
ifr.IfruHwaddr.Data[0] |= 0x2 // set local assignment bit (IEEE802)
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), syscall.SIOCSIFHWADDR, uintptr(unsafe.Pointer(&ifr))); err != 0 {
return err
}

View file

@ -1,9 +1,5 @@
package netlink
import (
"math/rand"
)
func randIfrDataByte() uint8 {
return uint8(rand.Intn(255))
func ifrDataByte(b byte) uint8 {
return uint8(b)
}

View file

@ -2,10 +2,6 @@
package netlink
import (
"math/rand"
)
func randIfrDataByte() int8 {
return int8(rand.Intn(255))
func ifrDataByte(b byte) int8 {
return int8(b)
}

View file

@ -115,6 +115,7 @@ func TestCreateVethPair(t *testing.T) {
if err := NetworkCreateVethPair(name1, name2); err != nil {
t.Fatal(err)
}
defer NetworkLinkDel(name1)
if _, err := net.InterfaceByName(name1); err != nil {
t.Fatal(err)
@ -124,3 +125,30 @@ func TestCreateVethPair(t *testing.T) {
t.Fatal(err)
}
}
func TestSetMACAddress(t *testing.T) {
if testing.Short() {
return
}
name := "testmac"
mac := randMacAddr()
if err := NetworkLinkAdd(name, "bridge"); err != nil {
t.Fatal(err)
}
defer NetworkLinkDel(name)
if err := NetworkSetMacAddress(name, mac); err != nil {
t.Fatal(err)
}
iface, err := net.InterfaceByName(name)
if err != nil {
t.Fatal(err)
}
if iface.HardwareAddr.String() != mac {
t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac)
}
}