Merge pull request #426 from vmarmol/fix-bug
Updating to latest libcontainer.
This commit is contained in:
commit
1cb300eecc
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -59,8 +59,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/libcontainer",
|
"ImportPath": "github.com/docker/libcontainer",
|
||||||
"Comment": "v1.2.0-173-g58fc931",
|
"Comment": "v1.4.0-52-gd7dea0e",
|
||||||
"Rev": "58fc93160e03387a4f41dcf4aed2e376c4a92db4"
|
"Rev": "d7dea0e925315bab640115053204c16718839b1e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||||
|
3
Godeps/_workspace/src/github.com/docker/libcontainer/Dockerfile
generated
vendored
3
Godeps/_workspace/src/github.com/docker/libcontainer/Dockerfile
generated
vendored
@ -1,6 +1,5 @@
|
|||||||
FROM crosbymichael/golang
|
FROM golang:1.4
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y gcc make
|
|
||||||
RUN go get golang.org/x/tools/cmd/cover
|
RUN go get golang.org/x/tools/cmd/cover
|
||||||
|
|
||||||
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
|
ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
|
||||||
|
1
Godeps/_workspace/src/github.com/docker/libcontainer/MAINTAINERS
generated
vendored
1
Godeps/_workspace/src/github.com/docker/libcontainer/MAINTAINERS
generated
vendored
@ -2,4 +2,5 @@ Michael Crosby <michael@docker.com> (@crosbymichael)
|
|||||||
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
||||||
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
||||||
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
||||||
|
Alexandr Morozov <lk4d4@docker.com> (@LK4D4)
|
||||||
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
6
Godeps/_workspace/src/github.com/docker/libcontainer/Makefile
generated
vendored
6
Godeps/_workspace/src/github.com/docker/libcontainer/Makefile
generated
vendored
@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
all:
|
all:
|
||||||
docker build -t docker/libcontainer .
|
docker build -t dockercore/libcontainer .
|
||||||
|
|
||||||
test:
|
test:
|
||||||
# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
|
# we need NET_ADMIN for the netlink tests and SYS_ADMIN for mounting
|
||||||
docker run --rm -it --privileged docker/libcontainer
|
docker run --rm -it --privileged dockercore/libcontainer
|
||||||
|
|
||||||
sh:
|
sh:
|
||||||
docker run --rm -it --privileged -w /busybox docker/libcontainer nsinit exec sh
|
docker run --rm -it --privileged -w /busybox dockercore/libcontainer nsinit exec sh
|
||||||
|
|
||||||
GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
|
GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
|
||||||
|
|
||||||
|
25
Godeps/_workspace/src/github.com/docker/libcontainer/SPEC.md
generated
vendored
25
Godeps/_workspace/src/github.com/docker/libcontainer/SPEC.md
generated
vendored
@ -318,4 +318,29 @@ a container.
|
|||||||
| Resume | Resume all processes inside the container if paused |
|
| Resume | Resume all processes inside the container if paused |
|
||||||
| Exec | Execute a new process inside of the container ( requires setns ) |
|
| Exec | Execute a new process inside of the container ( requires setns ) |
|
||||||
|
|
||||||
|
### Execute a new process inside of a running container.
|
||||||
|
|
||||||
|
User can execute a new process inside of a running container. Any binaries to be
|
||||||
|
executed must be accessible within the container's rootfs.
|
||||||
|
|
||||||
|
The started process will run inside the container's rootfs. Any changes
|
||||||
|
made by the process to the container's filesystem will persist after the
|
||||||
|
process finished executing.
|
||||||
|
|
||||||
|
The started process will join all the container's existing namespaces. When the
|
||||||
|
container is paused, the process will also be paused and will resume when
|
||||||
|
the container is unpaused. The started process will only run when the container's
|
||||||
|
primary process (PID 1) is running, and will not be restarted when the container
|
||||||
|
is restarted.
|
||||||
|
|
||||||
|
#### Planned additions
|
||||||
|
|
||||||
|
The started process will have its own cgroups nested inside the container's
|
||||||
|
cgroups. This is used for process tracking and optionally resource allocation
|
||||||
|
handling for the new process. Freezer cgroup is required, the rest of the cgroups
|
||||||
|
are optional. The process executor must place its pid inside the correct
|
||||||
|
cgroups before starting the process. This is done so that no child processes or
|
||||||
|
threads can escape the cgroups.
|
||||||
|
|
||||||
|
When the process is stopped, the process executor will try (in a best-effort way)
|
||||||
|
to stop all its children and remove the sub-cgroups.
|
||||||
|
2
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
2
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
@ -105,7 +105,7 @@ func GetStats(systemPaths map[string]string) (*cgroups.Stats, error) {
|
|||||||
stats := cgroups.NewStats()
|
stats := cgroups.NewStats()
|
||||||
for name, path := range systemPaths {
|
for name, path := range systemPaths {
|
||||||
sys, ok := subsystems[name]
|
sys, ok := subsystems[name]
|
||||||
if !ok {
|
if !ok || !cgroups.PathExists(path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := sys.GetStats(path, stats); err != nil {
|
if err := sys.GetStats(path, stats); err != nil {
|
||||||
|
9
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go
generated
vendored
9
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/memory.go
generated
vendored
@ -38,12 +38,17 @@ func (s *MemoryGroup) Set(d *data) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// By default, MemorySwap is set to twice the size of RAM.
|
// By default, MemorySwap is set to twice the size of RAM.
|
||||||
// If you want to omit MemorySwap, set it to `-1'.
|
// If you want to omit MemorySwap, set it to '-1'.
|
||||||
if d.c.MemorySwap != -1 {
|
if d.c.MemorySwap == 0 {
|
||||||
if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
|
if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if d.c.MemorySwap > 0 {
|
||||||
|
if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.MemorySwap, 10)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
6
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
6
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
@ -53,7 +53,7 @@ func expectBlkioStatsEquals(t *testing.T, expected, actual cgroups.BlkioStats) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := blkioStatEntryEquals(expected.IoMergedRecursive, actual.IoMergedRecursive); err != nil {
|
if err := blkioStatEntryEquals(expected.IoMergedRecursive, actual.IoMergedRecursive); err != nil {
|
||||||
log.Printf("blkio IoMergedRecursive do not match - %s vs %s\n", expected.IoMergedRecursive, actual.IoMergedRecursive)
|
log.Printf("blkio IoMergedRecursive do not match - %v vs %v\n", expected.IoMergedRecursive, actual.IoMergedRecursive)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,4 +90,8 @@ func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats)
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if expected.Failcnt != actual.Failcnt {
|
||||||
|
log.Printf("Expected memory failcnt %d but found %d\n", expected.Failcnt, actual.Failcnt)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
34
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/utils.go
generated
vendored
34
Godeps/_workspace/src/github.com/docker/libcontainer/cgroups/utils.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
)
|
)
|
||||||
@ -173,7 +174,7 @@ func ParseCgroupFile(subsystem string, r io.Reader) (string, error) {
|
|||||||
return "", NewNotFoundError(subsystem)
|
return "", NewNotFoundError(subsystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathExists(path string) bool {
|
func PathExists(path string) bool {
|
||||||
if _, err := os.Stat(path); err != nil {
|
if _, err := os.Stat(path); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -182,7 +183,7 @@ func pathExists(path string) bool {
|
|||||||
|
|
||||||
func EnterPid(cgroupPaths map[string]string, pid int) error {
|
func EnterPid(cgroupPaths map[string]string, pid int) error {
|
||||||
for _, path := range cgroupPaths {
|
for _, path := range cgroupPaths {
|
||||||
if pathExists(path) {
|
if PathExists(path) {
|
||||||
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
|
if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"),
|
||||||
[]byte(strconv.Itoa(pid)), 0700); err != nil {
|
[]byte(strconv.Itoa(pid)), 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -193,13 +194,30 @@ func EnterPid(cgroupPaths map[string]string, pid int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemovePaths iterates over the provided paths removing them.
|
// RemovePaths iterates over the provided paths removing them.
|
||||||
// If an error is encountered the removal proceeds and the first error is
|
// We trying to remove all paths five times with increasing delay between tries.
|
||||||
// returned to ensure a partial removal is not possible.
|
// If after all there are not removed cgroups - appropriate error will be
|
||||||
|
// returned.
|
||||||
func RemovePaths(paths map[string]string) (err error) {
|
func RemovePaths(paths map[string]string) (err error) {
|
||||||
for _, path := range paths {
|
delay := 10 * time.Millisecond
|
||||||
if rerr := os.RemoveAll(path); err == nil {
|
for i := 0; i < 5; i++ {
|
||||||
err = rerr
|
if i != 0 {
|
||||||
|
time.Sleep(delay)
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
for s, p := range paths {
|
||||||
|
os.RemoveAll(p)
|
||||||
|
// TODO: here probably should be logging
|
||||||
|
_, err := os.Stat(p)
|
||||||
|
// We need this strange way of checking cgroups existence because
|
||||||
|
// RemoveAll almost always returns error, even on already removed
|
||||||
|
// cgroups
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
delete(paths, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return fmt.Errorf("Failed to remove paths: %s", paths)
|
||||||
}
|
}
|
||||||
|
50
Godeps/_workspace/src/github.com/docker/libcontainer/config.go
generated
vendored
50
Godeps/_workspace/src/github.com/docker/libcontainer/config.go
generated
vendored
@ -10,11 +10,55 @@ type MountConfig mount.MountConfig
|
|||||||
|
|
||||||
type Network network.Network
|
type Network network.Network
|
||||||
|
|
||||||
|
type NamespaceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
NEWNET NamespaceType = "NEWNET"
|
||||||
|
NEWPID NamespaceType = "NEWPID"
|
||||||
|
NEWNS NamespaceType = "NEWNS"
|
||||||
|
NEWUTS NamespaceType = "NEWUTS"
|
||||||
|
NEWIPC NamespaceType = "NEWIPC"
|
||||||
|
NEWUSER NamespaceType = "NEWUSER"
|
||||||
|
)
|
||||||
|
|
||||||
// Namespace defines configuration for each namespace. It specifies an
|
// Namespace defines configuration for each namespace. It specifies an
|
||||||
// alternate path that is able to be joined via setns.
|
// alternate path that is able to be joined via setns.
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
Name string `json:"name"`
|
Type NamespaceType `json:"type"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Namespaces []Namespace
|
||||||
|
|
||||||
|
func (n *Namespaces) Remove(t NamespaceType) bool {
|
||||||
|
i := n.index(t)
|
||||||
|
if i == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*n = append((*n)[:i], (*n)[i+1:]...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) Add(t NamespaceType, path string) {
|
||||||
|
i := n.index(t)
|
||||||
|
if i == -1 {
|
||||||
|
*n = append(*n, Namespace{Type: t, Path: path})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
(*n)[i].Path = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) index(t NamespaceType) int {
|
||||||
|
for i, ns := range *n {
|
||||||
|
if ns.Type == t {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Namespaces) Contains(t NamespaceType) bool {
|
||||||
|
return n.index(t) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config defines configuration options for executing a process inside a contained environment.
|
// Config defines configuration options for executing a process inside a contained environment.
|
||||||
@ -45,7 +89,7 @@ type Config struct {
|
|||||||
|
|
||||||
// Namespaces specifies the container's namespaces that it should setup when cloning the init process
|
// Namespaces specifies the container's namespaces that it should setup when cloning the init process
|
||||||
// If a namespace is not provided that namespace is shared from the container's parent process
|
// If a namespace is not provided that namespace is shared from the container's parent process
|
||||||
Namespaces []Namespace `json:"namespaces,omitempty"`
|
Namespaces Namespaces `json:"namespaces,omitempty"`
|
||||||
|
|
||||||
// Capabilities specify the capabilities to keep when executing the process inside the container
|
// Capabilities specify the capabilities to keep when executing the process inside the container
|
||||||
// All capbilities not specified will be dropped from the processes capability mask
|
// All capbilities not specified will be dropped from the processes capability mask
|
||||||
|
19
Godeps/_workspace/src/github.com/docker/libcontainer/config_test.go
generated
vendored
19
Godeps/_workspace/src/github.com/docker/libcontainer/config_test.go
generated
vendored
@ -64,12 +64,12 @@ func TestConfigJsonFormat(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
if getNamespaceIndex(container, "NEWNET") == -1 {
|
if !container.Namespaces.Contains(NEWNET) {
|
||||||
t.Log("namespaces should contain NEWNET")
|
t.Log("namespaces should contain NEWNET")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
if getNamespaceIndex(container, "NEWUSER") != -1 {
|
if container.Namespaces.Contains(NEWUSER) {
|
||||||
t.Log("namespaces should not contain NEWUSER")
|
t.Log("namespaces should not contain NEWUSER")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@ -159,11 +159,14 @@ func TestSelinuxLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNamespaceIndex(config *Config, name string) int {
|
func TestRemoveNamespace(t *testing.T) {
|
||||||
for i, v := range config.Namespaces {
|
ns := Namespaces{
|
||||||
if v.Name == name {
|
{Type: NEWNET},
|
||||||
return i
|
}
|
||||||
}
|
if !ns.Remove(NEWNET) {
|
||||||
|
t.Fatal("NEWNET was not removed")
|
||||||
|
}
|
||||||
|
if len(ns) != 0 {
|
||||||
|
t.Fatalf("namespaces should have 0 items but reports %d", len(ns))
|
||||||
}
|
}
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
|
18
Godeps/_workspace/src/github.com/docker/libcontainer/integration/exec_test.go
generated
vendored
18
Godeps/_workspace/src/github.com/docker/libcontainer/integration/exec_test.go
generated
vendored
@ -88,8 +88,7 @@ func TestIPCHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := newTemplateConfig(rootfs)
|
config := newTemplateConfig(rootfs)
|
||||||
i := getNamespaceIndex(config, "NEWIPC")
|
config.Namespaces.Remove(libcontainer.NEWIPC)
|
||||||
config.Namespaces = append(config.Namespaces[:i], config.Namespaces[i+1:]...)
|
|
||||||
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
|
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -121,8 +120,7 @@ func TestIPCJoinPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := newTemplateConfig(rootfs)
|
config := newTemplateConfig(rootfs)
|
||||||
i := getNamespaceIndex(config, "NEWIPC")
|
config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipc")
|
||||||
config.Namespaces[i].Path = "/proc/1/ns/ipc"
|
|
||||||
|
|
||||||
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
|
buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -150,8 +148,7 @@ func TestIPCBadPath(t *testing.T) {
|
|||||||
defer remove(rootfs)
|
defer remove(rootfs)
|
||||||
|
|
||||||
config := newTemplateConfig(rootfs)
|
config := newTemplateConfig(rootfs)
|
||||||
i := getNamespaceIndex(config, "NEWIPC")
|
config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipcc")
|
||||||
config.Namespaces[i].Path = "/proc/1/ns/ipcc"
|
|
||||||
|
|
||||||
_, _, err = runContainer(config, "", "true")
|
_, _, err = runContainer(config, "", "true")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -179,12 +176,3 @@ func TestRlimit(t *testing.T) {
|
|||||||
t.Fatalf("expected rlimit to be 1024, got %s", limit)
|
t.Fatalf("expected rlimit to be 1024, got %s", limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNamespaceIndex(config *libcontainer.Config, name string) int {
|
|
||||||
for i, v := range config.Namespaces {
|
|
||||||
if v.Name == name {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
140
Godeps/_workspace/src/github.com/docker/libcontainer/integration/execin_test.go
generated
vendored
Normal file
140
Godeps/_workspace/src/github.com/docker/libcontainer/integration/execin_test.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer"
|
||||||
|
"github.com/docker/libcontainer/namespaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecIn(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs, err := newRootFs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer remove(rootfs)
|
||||||
|
|
||||||
|
config := newTemplateConfig(rootfs)
|
||||||
|
if err := writeConfig(config); err != nil {
|
||||||
|
t.Fatalf("failed to write config %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
containerCmd, statePath, containerErr := startLongRunningContainer(config)
|
||||||
|
defer func() {
|
||||||
|
// kill the container
|
||||||
|
if containerCmd.Process != nil {
|
||||||
|
containerCmd.Process.Kill()
|
||||||
|
}
|
||||||
|
if err := <-containerErr; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// start the exec process
|
||||||
|
state, err := libcontainer.GetState(statePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get state %s", err)
|
||||||
|
}
|
||||||
|
buffers := newStdBuffers()
|
||||||
|
execErr := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := namespaces.ExecIn(config, state, []string{"ps"},
|
||||||
|
os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr,
|
||||||
|
"", nil)
|
||||||
|
execErr <- err
|
||||||
|
}()
|
||||||
|
if err := <-execErr; err != nil {
|
||||||
|
t.Fatalf("exec finished with error %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := buffers.Stdout.String()
|
||||||
|
if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") {
|
||||||
|
t.Fatalf("unexpected running process, output %q", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecInRlimit(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs, err := newRootFs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer remove(rootfs)
|
||||||
|
|
||||||
|
config := newTemplateConfig(rootfs)
|
||||||
|
if err := writeConfig(config); err != nil {
|
||||||
|
t.Fatalf("failed to write config %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
containerCmd, statePath, containerErr := startLongRunningContainer(config)
|
||||||
|
defer func() {
|
||||||
|
// kill the container
|
||||||
|
if containerCmd.Process != nil {
|
||||||
|
containerCmd.Process.Kill()
|
||||||
|
}
|
||||||
|
if err := <-containerErr; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// start the exec process
|
||||||
|
state, err := libcontainer.GetState(statePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get state %s", err)
|
||||||
|
}
|
||||||
|
buffers := newStdBuffers()
|
||||||
|
execErr := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := namespaces.ExecIn(config, state, []string{"/bin/sh", "-c", "ulimit -n"},
|
||||||
|
os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr,
|
||||||
|
"", nil)
|
||||||
|
execErr <- err
|
||||||
|
}()
|
||||||
|
if err := <-execErr; err != nil {
|
||||||
|
t.Fatalf("exec finished with error %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := buffers.Stdout.String()
|
||||||
|
if limit := strings.TrimSpace(out); limit != "1024" {
|
||||||
|
t.Fatalf("expected rlimit to be 1024, got %s", limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start a long-running container so we have time to inspect execin processes
|
||||||
|
func startLongRunningContainer(config *libcontainer.Config) (*exec.Cmd, string, chan error) {
|
||||||
|
containerErr := make(chan error, 1)
|
||||||
|
containerCmd := &exec.Cmd{}
|
||||||
|
var statePath string
|
||||||
|
|
||||||
|
createCmd := func(container *libcontainer.Config, console, dataPath, init string,
|
||||||
|
pipe *os.File, args []string) *exec.Cmd {
|
||||||
|
containerCmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args)
|
||||||
|
statePath = dataPath
|
||||||
|
return containerCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
var containerStart sync.WaitGroup
|
||||||
|
containerStart.Add(1)
|
||||||
|
go func() {
|
||||||
|
buffers := newStdBuffers()
|
||||||
|
_, err := namespaces.Exec(config,
|
||||||
|
buffers.Stdin, buffers.Stdout, buffers.Stderr,
|
||||||
|
"", config.RootFs, []string{"sleep", "10"},
|
||||||
|
createCmd, containerStart.Done)
|
||||||
|
containerErr <- err
|
||||||
|
}()
|
||||||
|
containerStart.Wait()
|
||||||
|
|
||||||
|
return containerCmd, statePath, containerErr
|
||||||
|
}
|
67
Godeps/_workspace/src/github.com/docker/libcontainer/integration/init_test.go
generated
vendored
67
Godeps/_workspace/src/github.com/docker/libcontainer/integration/init_test.go
generated
vendored
@ -1,33 +1,76 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer"
|
||||||
"github.com/docker/libcontainer/namespaces"
|
"github.com/docker/libcontainer/namespaces"
|
||||||
|
_ "github.com/docker/libcontainer/namespaces/nsenter"
|
||||||
)
|
)
|
||||||
|
|
||||||
// init runs the libcontainer initialization code because of the busybox style needs
|
// init runs the libcontainer initialization code because of the busybox style needs
|
||||||
// to work around the go runtime and the issues with forking
|
// to work around the go runtime and the issues with forking
|
||||||
func init() {
|
func init() {
|
||||||
if len(os.Args) < 2 || os.Args[1] != "init" {
|
if len(os.Args) < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runtime.LockOSThread()
|
// handle init
|
||||||
|
if len(os.Args) >= 2 && os.Args[1] == "init" {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
container, err := loadConfig()
|
container, err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
|
||||||
|
log.Fatalf("unable to initialize for container: %s", err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := os.Getwd()
|
// handle execin
|
||||||
if err != nil {
|
if len(os.Args) >= 2 && os.Args[0] == "nsenter-exec" {
|
||||||
log.Fatal(err)
|
runtime.LockOSThread()
|
||||||
}
|
|
||||||
|
|
||||||
if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
|
// User args are passed after '--' in the command line.
|
||||||
log.Fatalf("unable to initialize for container: %s", err)
|
userArgs := findUserArgs()
|
||||||
|
|
||||||
|
config, err := loadConfigFromFd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := namespaces.FinalizeSetns(config, userArgs); err != nil {
|
||||||
|
log.Fatalf("docker-exec: failed to exec: %s", err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
}
|
||||||
|
|
||||||
|
func findUserArgs() []string {
|
||||||
|
for i, a := range os.Args {
|
||||||
|
if a == "--" {
|
||||||
|
return os.Args[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
||||||
|
// fd 3 when running a process
|
||||||
|
func loadConfigFromFd() (*libcontainer.Config, error) {
|
||||||
|
var config *libcontainer.Config
|
||||||
|
if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
}
|
}
|
||||||
|
14
Godeps/_workspace/src/github.com/docker/libcontainer/integration/template_test.go
generated
vendored
14
Godeps/_workspace/src/github.com/docker/libcontainer/integration/template_test.go
generated
vendored
@ -32,13 +32,13 @@ func newTemplateConfig(rootfs string) *libcontainer.Config {
|
|||||||
"KILL",
|
"KILL",
|
||||||
"AUDIT_WRITE",
|
"AUDIT_WRITE",
|
||||||
},
|
},
|
||||||
Namespaces: []libcontainer.Namespace{
|
Namespaces: libcontainer.Namespaces([]libcontainer.Namespace{
|
||||||
{Name: "NEWNS"},
|
{Type: libcontainer.NEWNS},
|
||||||
{Name: "NEWUTS"},
|
{Type: libcontainer.NEWUTS},
|
||||||
{Name: "NEWIPC"},
|
{Type: libcontainer.NEWIPC},
|
||||||
{Name: "NEWPID"},
|
{Type: libcontainer.NEWPID},
|
||||||
{Name: "NEWNET"},
|
{Type: libcontainer.NEWNET},
|
||||||
},
|
}),
|
||||||
Cgroups: &cgroups.Cgroup{
|
Cgroups: &cgroups.Cgroup{
|
||||||
Parent: "integration",
|
Parent: "integration",
|
||||||
AllowAllDevices: false,
|
AllowAllDevices: false,
|
||||||
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/execin.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/execin.go
generated
vendored
@ -97,6 +97,10 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := setupRlimits(container); err != nil {
|
||||||
|
return fmt.Errorf("setup rlimits %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := FinalizeNamespace(container); err != nil {
|
if err := FinalizeNamespace(container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
8
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/init.go
generated
vendored
8
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/init.go
generated
vendored
@ -178,17 +178,17 @@ func SetupUser(u string) error {
|
|||||||
Home: "/",
|
Home: "/",
|
||||||
}
|
}
|
||||||
|
|
||||||
passwdFile, err := user.GetPasswdFile()
|
passwdPath, err := user.GetPasswdPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groupFile, err := user.GetGroupFile()
|
groupPath, err := user.GetGroupPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
execUser, err := user.GetExecUserFile(u, &defaultExecUser, passwdFile, groupFile)
|
execUser, err := user.GetExecUserPath(u, &defaultExecUser, passwdPath, groupPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get supplementary groups %s", err)
|
return fmt.Errorf("get supplementary groups %s", err)
|
||||||
}
|
}
|
||||||
@ -318,7 +318,7 @@ func joinExistingNamespaces(namespaces []libcontainer.Namespace) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = system.Setns(f.Fd(), uintptr(namespaceInfo[ns.Name]))
|
err = system.Setns(f.Fd(), uintptr(namespaceInfo[ns.Type]))
|
||||||
f.Close()
|
f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
15
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c
generated
vendored
15
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c
generated
vendored
@ -32,8 +32,8 @@ void get_args(int *argc, char ***argv)
|
|||||||
contents_size += kBufSize;
|
contents_size += kBufSize;
|
||||||
contents = (char *)realloc(contents, contents_size);
|
contents = (char *)realloc(contents, contents_size);
|
||||||
bytes_read =
|
bytes_read =
|
||||||
read(fd, contents + contents_offset,
|
read(fd, contents + contents_offset,
|
||||||
contents_size - contents_offset);
|
contents_size - contents_offset);
|
||||||
contents_offset += bytes_read;
|
contents_offset += bytes_read;
|
||||||
}
|
}
|
||||||
while (bytes_read > 0);
|
while (bytes_read > 0);
|
||||||
@ -89,17 +89,20 @@ void nsenter()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef PR_SET_CHILD_SUBREAPER
|
||||||
if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
|
if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) {
|
||||||
fprintf(stderr, "nsenter: failed to set child subreaper: %s", strerror(errno));
|
fprintf(stderr, "nsenter: failed to set child subreaper: %s",
|
||||||
exit(1);
|
strerror(errno));
|
||||||
}
|
exit(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
{"nspid", required_argument, NULL, 'n'},
|
{"nspid", required_argument, NULL, 'n'},
|
||||||
{"console", required_argument, NULL, 't'},
|
{"console", required_argument, NULL, 't'},
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
pid_t init_pid = -1;
|
pid_t init_pid = -1;
|
||||||
char *init_pid_str = NULL;
|
char *init_pid_str = NULL;
|
||||||
char *console = NULL;
|
char *console = NULL;
|
||||||
|
18
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/utils.go
generated
vendored
18
Godeps/_workspace/src/github.com/docker/libcontainer/namespaces/utils.go
generated
vendored
@ -17,13 +17,13 @@ func (i initError) Error() string {
|
|||||||
return i.Message
|
return i.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
var namespaceInfo = map[string]int{
|
var namespaceInfo = map[libcontainer.NamespaceType]int{
|
||||||
"NEWNET": syscall.CLONE_NEWNET,
|
libcontainer.NEWNET: syscall.CLONE_NEWNET,
|
||||||
"NEWNS": syscall.CLONE_NEWNS,
|
libcontainer.NEWNS: syscall.CLONE_NEWNS,
|
||||||
"NEWUSER": syscall.CLONE_NEWUSER,
|
libcontainer.NEWUSER: syscall.CLONE_NEWUSER,
|
||||||
"NEWIPC": syscall.CLONE_NEWIPC,
|
libcontainer.NEWIPC: syscall.CLONE_NEWIPC,
|
||||||
"NEWUTS": syscall.CLONE_NEWUTS,
|
libcontainer.NEWUTS: syscall.CLONE_NEWUTS,
|
||||||
"NEWPID": syscall.CLONE_NEWPID,
|
libcontainer.NEWPID: syscall.CLONE_NEWPID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a newly initialized Pipe for communication between processes
|
// New returns a newly initialized Pipe for communication between processes
|
||||||
@ -37,9 +37,9 @@ func newInitPipe() (parent *os.File, child *os.File, err error) {
|
|||||||
|
|
||||||
// GetNamespaceFlags parses the container's Namespaces options to set the correct
|
// GetNamespaceFlags parses the container's Namespaces options to set the correct
|
||||||
// flags on clone, unshare, and setns
|
// flags on clone, unshare, and setns
|
||||||
func GetNamespaceFlags(namespaces []libcontainer.Namespace) (flag int) {
|
func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) {
|
||||||
for _, v := range namespaces {
|
for _, v := range namespaces {
|
||||||
flag |= namespaceInfo[v.Name]
|
flag |= namespaceInfo[v.Type]
|
||||||
}
|
}
|
||||||
return flag
|
return flag
|
||||||
}
|
}
|
||||||
|
5
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
5
Godeps/_workspace/src/github.com/docker/libcontainer/netlink/netlink_linux.go
generated
vendored
@ -522,11 +522,10 @@ func NetworkSetMacAddress(iface *net.Interface, macaddr string) error {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
MULTICAST byte = 0x1
|
MULTICAST byte = 0x1
|
||||||
LOCALOUI byte = 0x2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if hwaddr[0]&0x1 == MULTICAST || hwaddr[0]&0x2 != LOCALOUI {
|
if hwaddr[0]&0x1 == MULTICAST {
|
||||||
return fmt.Errorf("Incorrect Local MAC Address specified: %s", macaddr)
|
return fmt.Errorf("Multicast MAC Address is not supported: %s", macaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
12
Godeps/_workspace/src/github.com/docker/libcontainer/network/network.go
generated
vendored
12
Godeps/_workspace/src/github.com/docker/libcontainer/network/network.go
generated
vendored
@ -88,6 +88,18 @@ func SetInterfaceIp(name string, rawIp string) error {
|
|||||||
return netlink.NetworkLinkAddIp(iface, ip, ipNet)
|
return netlink.NetworkLinkAddIp(iface, ip, ipNet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteInterfaceIp(name string, rawIp string) error {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ip, ipNet, err := net.ParseCIDR(rawIp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.NetworkLinkDelIp(iface, ip, ipNet)
|
||||||
|
}
|
||||||
|
|
||||||
func SetMtu(name string, mtu int) error {
|
func SetMtu(name string, mtu int) error {
|
||||||
iface, err := net.InterfaceByName(name)
|
iface, err := net.InterfaceByName(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package fs
|
package libcontainer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/cgroups"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotifyOnOOM sends signals on the returned channel when the cgroup reaches
|
const oomCgroupName = "memory"
|
||||||
// its memory limit. The channel is closed when the cgroup is removed.
|
|
||||||
func NotifyOnOOM(c *cgroups.Cgroup) (<-chan struct{}, error) {
|
// NotifyOnOOM returns channel on which you can expect event about OOM,
|
||||||
d, err := getCgroupData(c, 0)
|
// if process died without OOM this channel will be closed.
|
||||||
|
// s is current *libcontainer.State for container.
|
||||||
|
func NotifyOnOOM(s *State) (<-chan struct{}, error) {
|
||||||
|
dir := s.CgroupPaths[oomCgroupName]
|
||||||
|
if dir == "" {
|
||||||
|
return nil, fmt.Errorf("There is no path for %q in state", oomCgroupName)
|
||||||
|
}
|
||||||
|
oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return notifyOnOOM(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func notifyOnOOM(d *data) (<-chan struct{}, error) {
|
|
||||||
dir, err := d.path("memory")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
|
fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0)
|
||||||
if syserr != 0 {
|
if syserr != 0 {
|
||||||
return nil, syserr
|
return nil, syserr
|
||||||
@ -35,48 +31,32 @@ func notifyOnOOM(d *data) (<-chan struct{}, error) {
|
|||||||
|
|
||||||
eventfd := os.NewFile(fd, "eventfd")
|
eventfd := os.NewFile(fd, "eventfd")
|
||||||
|
|
||||||
oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control"))
|
eventControlPath := filepath.Join(dir, "cgroup.event_control")
|
||||||
if err != nil {
|
data := fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
|
||||||
eventfd.Close()
|
if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
eventControlPath = filepath.Join(dir, "cgroup.event_control")
|
|
||||||
data = fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := writeFile(dir, "cgroup.event_control", data); err != nil {
|
|
||||||
eventfd.Close()
|
eventfd.Close()
|
||||||
oomControl.Close()
|
oomControl.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
close(ch)
|
close(ch)
|
||||||
eventfd.Close()
|
eventfd.Close()
|
||||||
oomControl.Close()
|
oomControl.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
buf := make([]byte, 8)
|
buf := make([]byte, 8)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if _, err := eventfd.Read(buf); err != nil {
|
if _, err := eventfd.Read(buf); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a cgroup is destroyed, an event is sent to eventfd.
|
// When a cgroup is destroyed, an event is sent to eventfd.
|
||||||
// So if the control path is gone, return instead of notifying.
|
// So if the control path is gone, return instead of notifying.
|
||||||
if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
|
if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ch <- struct{}{}
|
ch <- struct{}{}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return ch, nil
|
return ch, nil
|
||||||
}
|
}
|
@ -1,38 +1,48 @@
|
|||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package fs
|
package libcontainer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNotifyOnOOM(t *testing.T) {
|
func TestNotifyOnOOM(t *testing.T) {
|
||||||
helper := NewCgroupTestUtil("memory", t)
|
memoryPath, err := ioutil.TempDir("", "testnotifyoom-")
|
||||||
defer helper.cleanup()
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
helper.writeFileContents(map[string]string{
|
}
|
||||||
"memory.oom_control": "",
|
oomPath := filepath.Join(memoryPath, "memory.oom_control")
|
||||||
"cgroup.event_control": "",
|
eventPath := filepath.Join(memoryPath, "cgroup.event_control")
|
||||||
})
|
if err := ioutil.WriteFile(oomPath, []byte{}, 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(eventPath, []byte{}, 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
var eventFd, oomControlFd int
|
var eventFd, oomControlFd int
|
||||||
|
st := &State{
|
||||||
ooms, err := notifyOnOOM(helper.CgroupData)
|
CgroupPaths: map[string]string{
|
||||||
|
"memory": memoryPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ooms, err := NotifyOnOOM(st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("expected no error, got:", err)
|
t.Fatal("expected no error, got:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryPath, _ := helper.CgroupData.path("memory")
|
data, err := ioutil.ReadFile(eventPath)
|
||||||
data, err := readFile(memoryPath, "cgroup.event_control")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("couldn't read event control file:", err)
|
t.Fatal("couldn't read event control file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := fmt.Sscanf(data, "%d %d", &eventFd, &oomControlFd); err != nil {
|
if _, err := fmt.Sscanf(string(data), "%d %d", &eventFd, &oomControlFd); err != nil {
|
||||||
t.Fatalf("invalid control data %q: %s", data, err)
|
t.Fatalf("invalid control data %q: %s", data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +72,9 @@ func TestNotifyOnOOM(t *testing.T) {
|
|||||||
|
|
||||||
// simulate what happens when a cgroup is destroyed by cleaning up and then
|
// simulate what happens when a cgroup is destroyed by cleaning up and then
|
||||||
// writing to the eventfd.
|
// writing to the eventfd.
|
||||||
helper.cleanup()
|
if err := os.RemoveAll(memoryPath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
if _, err := syscall.Write(efd, buf); err != nil {
|
if _, err := syscall.Write(efd, buf); err != nil {
|
||||||
t.Fatal("unable to write to eventfd:", err)
|
t.Fatal("unable to write to eventfd:", err)
|
||||||
}
|
}
|
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/apparmor.json
generated
vendored
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/apparmor.json
generated
vendored
@ -177,11 +177,11 @@
|
|||||||
],
|
],
|
||||||
"hostname": "koye",
|
"hostname": "koye",
|
||||||
"namespaces": [
|
"namespaces": [
|
||||||
{"name":"NEWIPC"},
|
{"type":"NEWIPC"},
|
||||||
{"name": "NEWNET"},
|
{"type": "NEWNET"},
|
||||||
{"name": "NEWNS"},
|
{"type": "NEWNS"},
|
||||||
{"name": "NEWPID"},
|
{"type": "NEWPID"},
|
||||||
{"name": "NEWUTS"}
|
{"type": "NEWUTS"}
|
||||||
],
|
],
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
|
@ -176,11 +176,11 @@
|
|||||||
],
|
],
|
||||||
"hostname": "koye",
|
"hostname": "koye",
|
||||||
"namespaces": [
|
"namespaces": [
|
||||||
{"name": "NEWIPC"},
|
{"type": "NEWIPC"},
|
||||||
{"name": "NEWNET"},
|
{"type": "NEWNET"},
|
||||||
{"name": "NEWNS"},
|
{"type": "NEWNS"},
|
||||||
{"name": "NEWPID"},
|
{"type": "NEWPID"},
|
||||||
{"name": "NEWUTS"}
|
{"type": "NEWUTS"}
|
||||||
],
|
],
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
|
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/minimal.json
generated
vendored
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/minimal.json
generated
vendored
@ -182,11 +182,11 @@
|
|||||||
],
|
],
|
||||||
"hostname": "koye",
|
"hostname": "koye",
|
||||||
"namespaces": [
|
"namespaces": [
|
||||||
{"name": "NEWIPC"},
|
{"type": "NEWIPC"},
|
||||||
{"name": "NEWNET"},
|
{"type": "NEWNET"},
|
||||||
{"name": "NEWNS"},
|
{"type": "NEWNS"},
|
||||||
{"name": "NEWPID"},
|
{"type": "NEWPID"},
|
||||||
{"name": "NEWUTS"}
|
{"type": "NEWUTS"}
|
||||||
],
|
],
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
|
@ -176,11 +176,11 @@
|
|||||||
],
|
],
|
||||||
"hostname": "koye",
|
"hostname": "koye",
|
||||||
"namespaces": [
|
"namespaces": [
|
||||||
{"name": "NEWIPC"},
|
{"type": "NEWIPC"},
|
||||||
{"name": "NEWNET"},
|
{"type": "NEWNET"},
|
||||||
{"name": "NEWNS"},
|
{"type": "NEWNS"},
|
||||||
{"name": "NEWPID"},
|
{"type": "NEWPID"},
|
||||||
{"name": "NEWUTS"}
|
{"type": "NEWUTS"}
|
||||||
],
|
],
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
|
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/selinux.json
generated
vendored
10
Godeps/_workspace/src/github.com/docker/libcontainer/sample_configs/selinux.json
generated
vendored
@ -178,11 +178,11 @@
|
|||||||
],
|
],
|
||||||
"hostname": "koye",
|
"hostname": "koye",
|
||||||
"namespaces": [
|
"namespaces": [
|
||||||
{"name": "NEWIPC"},
|
{"type": "NEWIPC"},
|
||||||
{"name": "NEWNET"},
|
{"type": "NEWNET"},
|
||||||
{"name": "NEWNS"},
|
{"type": "NEWNS"},
|
||||||
{"name": "NEWPID"},
|
{"type": "NEWPID"},
|
||||||
{"name": "NEWUTS"}
|
{"type": "NEWUTS"}
|
||||||
],
|
],
|
||||||
"networks": [
|
"networks": [
|
||||||
{
|
{
|
||||||
|
1
Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
1
Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
@ -1 +1,2 @@
|
|||||||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
Aleksa Sarai <cyphar@cyphar.com> (@cyphar)
|
||||||
|
16
Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unix.go
generated
vendored
16
Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unix.go
generated
vendored
@ -9,22 +9,22 @@ import (
|
|||||||
|
|
||||||
// Unix-specific path to the passwd and group formatted files.
|
// Unix-specific path to the passwd and group formatted files.
|
||||||
const (
|
const (
|
||||||
unixPasswdFile = "/etc/passwd"
|
unixPasswdPath = "/etc/passwd"
|
||||||
unixGroupFile = "/etc/group"
|
unixGroupPath = "/etc/group"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetPasswdFile() (string, error) {
|
func GetPasswdPath() (string, error) {
|
||||||
return unixPasswdFile, nil
|
return unixPasswdPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPasswd() (io.ReadCloser, error) {
|
func GetPasswd() (io.ReadCloser, error) {
|
||||||
return os.Open(unixPasswdFile)
|
return os.Open(unixPasswdPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGroupFile() (string, error) {
|
func GetGroupPath() (string, error) {
|
||||||
return unixGroupFile, nil
|
return unixGroupPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGroup() (io.ReadCloser, error) {
|
func GetGroup() (io.ReadCloser, error) {
|
||||||
return os.Open(unixGroupFile)
|
return os.Open(unixGroupPath)
|
||||||
}
|
}
|
||||||
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unsupported.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unsupported.go
generated
vendored
@ -4,7 +4,7 @@ package user
|
|||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
func GetPasswdFile() (string, error) {
|
func GetPasswdPath() (string, error) {
|
||||||
return "", ErrUnsupported
|
return "", ErrUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ func GetPasswd() (io.ReadCloser, error) {
|
|||||||
return nil, ErrUnsupported
|
return nil, ErrUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGroupFile() (string, error) {
|
func GetGroupPath() (string, error) {
|
||||||
return "", ErrUnsupported
|
return "", ErrUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
4
Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
@ -197,11 +197,11 @@ type ExecUser struct {
|
|||||||
Home string
|
Home string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the
|
// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
|
||||||
// given file paths and uses that data as the arguments to GetExecUser. If the
|
// given file paths and uses that data as the arguments to GetExecUser. If the
|
||||||
// files cannot be opened for any reason, the error is ignored and a nil
|
// files cannot be opened for any reason, the error is ignored and a nil
|
||||||
// io.Reader is passed instead.
|
// io.Reader is passed instead.
|
||||||
func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||||
passwd, err := os.Open(passwdPath)
|
passwd, err := os.Open(passwdPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
passwd = nil
|
passwd = nil
|
||||||
|
@ -161,7 +161,7 @@ func (self *dockerContainerHandler) readLibcontainerConfig() (*libcontainer.Conf
|
|||||||
config = oldConfig.Config
|
config = oldConfig.Config
|
||||||
for ns := range oldConfig.OldNamespaces {
|
for ns := range oldConfig.OldNamespaces {
|
||||||
config.Namespaces = append(config.Namespaces, libcontainer.Namespace{
|
config.Namespaces = append(config.Namespaces, libcontainer.Namespace{
|
||||||
Name: ns,
|
Type: libcontainer.NamespaceType(ns),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user