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

Update libcontainer to aab3f6d17f2f56606f07f3a6eb6

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2014-10-23 19:00:05 +00:00
parent 8b40d385b5
commit 291b84610f
25 changed files with 452 additions and 20 deletions

View file

@ -64,7 +64,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 8d1d0ba38a7348c5cfdc05aea3be34d75aadc8de
clone git github.com/docker/libcontainer aab3f6d17f2f56606f07f3a6eb6b693303f75812
# 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

@ -6,7 +6,7 @@ feels wrong or incomplete.
## Reporting Issues
When reporting [issues](https://github.com/docker/libcontainer/issues)
When reporting [issues](https://github.com/docker/libcontainer/issues)
on GitHub please include your host OS (Ubuntu 12.04, Fedora 19, etc),
the output of `uname -a`. Please include the steps required to reproduce
the problem if possible and applicable.
@ -14,7 +14,60 @@ This information will help us review and fix your issue faster.
## Development Environment
*Add instructions on setting up the development environment.*
### Requirements
For best results, use a Linux development environment.
The following packages are required to compile libcontainer natively.
- Golang 1.3
- GCC
- git
- cgutils
You can develop on OSX, but you are limited to Dockerfile-based builds only.
### Building libcontainer from Dockerfile
make all
This is the easiest way of building libcontainer.
As this build is done using Docker, you can even run this from [OSX](https://github.com/boot2docker/boot2docker)
### Testing changes with "nsinit"
make sh
This will create an container that runs `nsinit exec sh` on a busybox rootfs with the configuration from ['minimal.json'](https://github.com/docker/libcontainer/blob/master/sample_configs/minimal.json).
Like the previous command, you can run this on OSX too!
### Building libcontainer directly
> Note: You should add the `vendor` directory to your GOPATH to use the vendored libraries
./update-vendor.sh
go get -d ./...
make direct-build
# Run the tests
make direct-test-short | egrep --color 'FAIL|$'
# Run all the test
make direct-test | egrep --color 'FAIL|$'
### Testing Changes with "nsinit" directly
To test a change:
# Install nsinit
make direct-install
# Optional, add a docker0 bridge
ip link add docker0 type bridge
ifconfig docker0 172.17.0.1/16 up
mkdir testfs
curl -sSL https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.02/rootfs.tar | tar -xC testfs
cd testfs
cp <your-sample-config.json> container.json
nsinit exec sh
## Contribution Guidelines

View file

@ -3,6 +3,9 @@ FROM crosbymichael/golang
RUN apt-get update && apt-get install -y gcc make
RUN go get code.google.com/p/go.tools/cmd/cover
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
RUN go get github.com/docker/docker/pkg/term
# setup a playground for us to spawn containers in
RUN mkdir /busybox && \
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.02/rootfs.tar' | tar -xC /busybox
@ -14,8 +17,6 @@ COPY . /go/src/github.com/docker/libcontainer
WORKDIR /go/src/github.com/docker/libcontainer
RUN cp sample_configs/minimal.json /busybox/container.json
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
RUN go get -d -v ./...
RUN make direct-install

View file

@ -73,6 +73,19 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
return d, nil
}
// Symmetrical public function to update device based cgroups. Also available
// in the systemd implementation.
func ApplyDevices(c *cgroups.Cgroup, pid int) error {
d, err := getCgroupData(c, pid)
if err != nil {
return err
}
devices := subsystems["devices"]
return devices.Set(d)
}
func Cleanup(c *cgroups.Cgroup) error {
d, err := getCgroupData(c, 0)
if err != nil {

View file

@ -20,6 +20,10 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
return nil, fmt.Errorf("Systemd not supported")
}
func ApplyDevices(c *cgroups.Cgroup, pid int) error {
return fmt.Errorf("Systemd not supported")
}
func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
return fmt.Errorf("Systemd not supported")
}

View file

@ -327,6 +327,12 @@ func joinDevices(c *cgroups.Cgroup, pid int) error {
return nil
}
// Symmetrical public function to update device based cgroups. Also available
// in the fs implementation.
func ApplyDevices(c *cgroups.Cgroup, pid int) error {
return joinDevices(c, pid)
}
func joinMemory(c *cgroups.Cgroup, pid int) error {
memorySwap := c.MemorySwap

View file

@ -67,14 +67,14 @@ func OpenAndDup(consolePath string) error {
// Unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
// Unlockpt should be called before opening the slave side of a pseudoterminal.
func Unlockpt(f *os.File) error {
var u int
var u int32
return Ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
}
// Ptsname retrieves the name of the first available pts for the given master.
func Ptsname(f *os.File) (string, error) {
var n int
var n int32
if err := Ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
return "", err

View file

@ -0,0 +1,2 @@
// integration is used for integration testing of libcontainer
package integration

View file

@ -0,0 +1,38 @@
package integration
import (
"strings"
"testing"
)
func TestExecPS(t *testing.T) {
if testing.Short() {
return
}
rootfs, err := newRootFs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)
config := newTemplateConfig(rootfs)
buffers, exitCode, err := runContainer(config, "", "ps")
if err != nil {
t.Fatal(err)
}
if exitCode != 0 {
t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
}
lines := strings.Split(buffers.Stdout.String(), "\n")
if len(lines) < 2 {
t.Fatalf("more than one process running for output %q", buffers.Stdout.String())
}
expected := `1 root ps`
actual := strings.Trim(lines[1], "\n ")
if actual != expected {
t.Fatalf("expected output %q but received %q", expected, actual)
}
}

View file

@ -0,0 +1,39 @@
package integration
import (
"log"
"os"
"runtime"
"github.com/docker/libcontainer/namespaces"
"github.com/docker/libcontainer/syncpipe"
)
// init runs the libcontainer initialization code because of the busybox style needs
// to work around the go runtime and the issues with forking
func init() {
if len(os.Args) < 2 || os.Args[1] != "init" {
return
}
runtime.LockOSThread()
container, err := loadConfig()
if err != nil {
log.Fatal(err)
}
rootfs, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
if err != nil {
log.Fatalf("unable to create sync pipe: %s", err)
}
if err := namespaces.Init(container, rootfs, "", syncPipe, os.Args[3:]); err != nil {
log.Fatalf("unable to initialize for container: %s", err)
}
os.Exit(1)
}

View file

@ -0,0 +1,64 @@
package integration
import (
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/devices"
)
// newTemplateConfig returns a base template for running a container
//
// it uses a network strategy of just setting a loopback interface
// and the default setup for devices
func newTemplateConfig(rootfs string) *libcontainer.Config {
return &libcontainer.Config{
RootFs: rootfs,
Tty: false,
Capabilities: []string{
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SETFCAP",
"SETPCAP",
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL",
"AUDIT_WRITE",
},
Namespaces: map[string]bool{
"NEWNS": true,
"NEWUTS": true,
"NEWIPC": true,
"NEWPID": true,
"NEWNET": true,
},
Cgroups: &cgroups.Cgroup{
Parent: "integration",
AllowAllDevices: false,
AllowedDevices: devices.DefaultAllowedDevices,
},
MountConfig: &libcontainer.MountConfig{
DeviceNodes: devices.DefaultAutoCreatedDevices,
},
Hostname: "integration",
Env: []string{
"HOME=/root",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=integration",
"TERM=xterm",
},
Networks: []*libcontainer.Network{
{
Type: "loopback",
Address: "127.0.0.1/0",
Gateway: "localhost",
},
},
}
}

View file

@ -0,0 +1,95 @@
package integration
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/namespaces"
)
func newStdBuffers() *stdBuffers {
return &stdBuffers{
Stdin: bytes.NewBuffer(nil),
Stdout: bytes.NewBuffer(nil),
Stderr: bytes.NewBuffer(nil),
}
}
type stdBuffers struct {
Stdin *bytes.Buffer
Stdout *bytes.Buffer
Stderr *bytes.Buffer
}
func writeConfig(config *libcontainer.Config) error {
f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(config)
}
func loadConfig() (*libcontainer.Config, error) {
f, err := os.Open(filepath.Join(os.Getenv("data_path"), "container.json"))
if err != nil {
return nil, err
}
defer f.Close()
var container *libcontainer.Config
if err := json.NewDecoder(f).Decode(&container); err != nil {
return nil, err
}
return container, nil
}
// newRootFs creates a new tmp directory and copies the busybox root filesystem
func newRootFs() (string, error) {
dir, err := ioutil.TempDir("", "")
if err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0700); err != nil {
return "", err
}
if err := copyBusybox(dir); err != nil {
return "", nil
}
return dir, nil
}
func remove(dir string) {
os.RemoveAll(dir)
}
// copyBusybox copies the rootfs for a busybox container created for the test image
// into the new directory for the specific test
func copyBusybox(dest string) error {
out, err := exec.Command("sh", "-c", fmt.Sprintf("cp -R /busybox/* %s/", dest)).CombinedOutput()
if err != nil {
return fmt.Errorf("copy error %q: %q", err, out)
}
return nil
}
// runContainer runs the container with the specific config and arguments
//
// buffers are returned containing the STDOUT and STDERR output for the run
// along with the exit code and any go error
func runContainer(config *libcontainer.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) {
if err := writeConfig(config); err != nil {
return nil, -1, err
}
buffers = newStdBuffers()
exitCode, err = namespaces.Exec(config, buffers.Stdin, buffers.Stdout, buffers.Stderr,
console, config.RootFs, args, namespaces.DefaultCreateCommand, nil)
return
}

View file

@ -681,7 +681,7 @@ func NetworkChangeName(iface *net.Interface, newName string) error {
// Add a new VETH pair link on the host
// This is identical to running: ip link add name $name type veth peer name $peername
func NetworkCreateVethPair(name1, name2 string) error {
func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error {
s, err := getNetlinkSocket()
if err != nil {
return err
@ -696,6 +696,11 @@ func NetworkCreateVethPair(name1, name2 string) error {
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1))
wb.AddData(nameData)
txqLen := make([]byte, 4)
native.PutUint32(txqLen, uint32(txQueueLen))
txqData := newRtAttr(syscall.IFLA_TXQLEN, txqLen)
wb.AddData(txqData)
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth"))
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
@ -704,6 +709,10 @@ func NetworkCreateVethPair(name1, name2 string) error {
newIfInfomsgChild(nest3, syscall.AF_UNSPEC)
newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2))
txqLen2 := make([]byte, 4)
native.PutUint32(txqLen2, uint32(txQueueLen))
newRtAttrChild(nest3, syscall.IFLA_TXQLEN, txqLen2)
wb.AddData(nest1)
if err := s.Send(wb); err != nil {

View file

@ -290,7 +290,7 @@ func TestCreateVethPair(t *testing.T) {
name2 = "veth2"
)
if err := NetworkCreateVethPair(name1, name2); err != nil {
if err := NetworkCreateVethPair(name1, name2, 0); err != nil {
t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err)
}
defer NetworkLinkDel(name1)

View file

@ -47,7 +47,7 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error {
return ErrNotImplemented
}
func NetworkCreateVethPair(name1, name2 string) error {
func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error {
return ErrNotImplemented
}

View file

@ -32,8 +32,8 @@ func ChangeInterfaceName(old, newName string) error {
return netlink.NetworkChangeName(iface, newName)
}
func CreateVethPair(name1, name2 string) error {
return netlink.NetworkCreateVethPair(name1, name2)
func CreateVethPair(name1, name2 string, txQueueLen int) error {
return netlink.NetworkCreateVethPair(name1, name2, txQueueLen)
}
func SetInterfaceInNamespacePid(name string, nsPid int) error {

View file

@ -36,6 +36,11 @@ type Network struct {
// container's interfaces if a pair is created, specifically in the case of type veth
// Note: This does not apply to loopback interfaces.
Mtu int `json:"mtu,omitempty"`
// TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and
// container's interfaces if a pair is created, specifically in the case of type veth
// Note: This does not apply to loopback interfaces.
TxQueueLen int `json:"txqueuelen,omitempty"`
}
// Struct describing the network specific runtime state that will be maintained by libcontainer for all running containers

View file

@ -19,8 +19,9 @@ const defaultDevice = "eth0"
func (v *Veth) Create(n *Network, nspid int, networkState *NetworkState) error {
var (
bridge = n.Bridge
prefix = n.VethPrefix
bridge = n.Bridge
prefix = n.VethPrefix
txQueueLen = n.TxQueueLen
)
if bridge == "" {
return fmt.Errorf("bridge is not specified")
@ -28,7 +29,7 @@ func (v *Veth) Create(n *Network, nspid int, networkState *NetworkState) error {
if prefix == "" {
return fmt.Errorf("veth prefix is not specified")
}
name1, name2, err := createVethPair(prefix)
name1, name2, err := createVethPair(prefix, txQueueLen)
if err != nil {
return err
}
@ -96,7 +97,7 @@ func (v *Veth) Initialize(config *Network, networkState *NetworkState) error {
// createVethPair will automatically generage two random names for
// the veth pair and ensure that they have been created
func createVethPair(prefix string) (name1 string, name2 string, err error) {
func createVethPair(prefix string, txQueueLen int) (name1 string, name2 string, err error) {
for i := 0; i < 10; i++ {
if name1, err = utils.GenerateRandomName(prefix, 7); err != nil {
return
@ -106,7 +107,7 @@ func createVethPair(prefix string) (name1 string, name2 string, err error) {
return
}
if err = CreateVethPair(name1, name2); err != nil {
if err = CreateVethPair(name1, name2, txQueueLen); err != nil {
if err == netlink.ErrInterfaceExists {
continue
}

View file

@ -15,7 +15,7 @@ func TestGenerateVethNames(t *testing.T) {
prefix := "veth"
name1, name2, err := createVethPair(prefix)
name1, name2, err := createVethPair(prefix, 0)
if err != nil {
t.Fatal(err)
}
@ -36,13 +36,13 @@ func TestCreateDuplicateVethPair(t *testing.T) {
prefix := "veth"
name1, name2, err := createVethPair(prefix)
name1, name2, err := createVethPair(prefix, 0)
if err != nil {
t.Fatal(err)
}
// retry to create the name interfaces and make sure that we get the correct error
err = CreateVethPair(name1, name2)
err = CreateVethPair(name1, name2, 0)
if err == nil {
t.Fatal("expected error to not be nil with duplicate interface")
}

View file

@ -0,0 +1,24 @@
// +build linux,386
package system
import (
"syscall"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
return
}
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
return
}

View file

@ -0,0 +1,24 @@
// +build linux,amd64
package system
import (
"syscall"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
return
}
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
return
}

View file

@ -0,0 +1,24 @@
// +build linux,arm
package system
import (
"syscall"
)
// Setuid sets the uid of the calling thread to the specified uid.
func Setuid(uid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
return
}
// Setgid sets the gid of the calling thread to the specified gid.
func Setgid(gid int) (err error) {
_, _, e1 := syscall.RawSyscall(syscall.SYS_SETGID32, uintptr(gid), 0, 0)
if e1 != 0 {
err = e1
}
return
}

View file

@ -0,0 +1,15 @@
package utils
import "testing"
func TestGenerateName(t *testing.T) {
name, err := GenerateRandomName("veth", 5)
if err != nil {
t.Fatal(err)
}
expected := 5 + len("veth")
if len(name) != 5+len("veth") {
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
}
}

View file

@ -0,0 +1,15 @@
// +build !linux
package xattr
func Listxattr(path string) ([]string, error) {
return nil, ErrNotSupportedPlatform
}
func Getxattr(path, attr string) (string, error) {
return "", ErrNotSupportedPlatform
}
func Setxattr(path, xattr, value string) error {
return ErrNotSupportedPlatform
}