Includes mqueue label fix and cgroups improvements.
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
| ... | ... |
@@ -68,7 +68,7 @@ if [ "$1" = '--go' ]; then |
| 68 | 68 |
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar |
| 69 | 69 |
fi |
| 70 | 70 |
|
| 71 |
-clone git github.com/docker/libcontainer dd3cb8822352fd4acc0b8b426bd86e47e98f6853 |
|
| 71 |
+clone git github.com/docker/libcontainer aa10040b570386c1ae311c6245b9e21295b2b83a |
|
| 72 | 72 |
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) |
| 73 | 73 |
rm -rf src/github.com/docker/libcontainer/vendor |
| 74 | 74 |
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')" |
| ... | ... |
@@ -54,12 +54,10 @@ func getCgroupRoot() (string, error) {
|
| 54 | 54 |
return cgroupRoot, nil |
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 |
- // we can pick any subsystem to find the root |
|
| 58 |
- cpuRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
|
| 57 |
+ root, err := cgroups.FindCgroupMountpointDir() |
|
| 59 | 58 |
if err != nil {
|
| 60 | 59 |
return "", err |
| 61 | 60 |
} |
| 62 |
- root := filepath.Dir(cpuRoot) |
|
| 63 | 61 |
|
| 64 | 62 |
if _, err := os.Stat(root); err != nil {
|
| 65 | 63 |
return "", err |
| ... | ... |
@@ -231,6 +229,12 @@ func (raw *data) parent(subsystem string) (string, error) {
|
| 231 | 231 |
} |
| 232 | 232 |
|
| 233 | 233 |
func (raw *data) path(subsystem string) (string, error) {
|
| 234 |
+ _, err := cgroups.FindCgroupMountpoint(subsystem) |
|
| 235 |
+ // If we didn't mount the subsystem, there is no point we make the path. |
|
| 236 |
+ if err != nil {
|
|
| 237 |
+ return "", err |
|
| 238 |
+ } |
|
| 239 |
+ |
|
| 234 | 240 |
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process. |
| 235 | 241 |
if filepath.IsAbs(raw.cgroup) {
|
| 236 | 242 |
path := filepath.Join(raw.root, subsystem, raw.cgroup) |
| ... | ... |
@@ -17,8 +17,12 @@ type BlkioGroup struct {
|
| 17 | 17 |
|
| 18 | 18 |
func (s *BlkioGroup) Apply(d *data) error {
|
| 19 | 19 |
dir, err := d.join("blkio")
|
| 20 |
- if err != nil && !cgroups.IsNotFound(err) {
|
|
| 21 |
- return err |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ if cgroups.IsNotFound(err) {
|
|
| 22 |
+ return nil |
|
| 23 |
+ } else {
|
|
| 24 |
+ return err |
|
| 25 |
+ } |
|
| 22 | 26 |
} |
| 23 | 27 |
|
| 24 | 28 |
if err := s.Set(dir, d.c); err != nil {
|
| ... | ... |
@@ -18,7 +18,11 @@ func (s *CpuGroup) Apply(d *data) error {
|
| 18 | 18 |
// on a container basis |
| 19 | 19 |
dir, err := d.join("cpu")
|
| 20 | 20 |
if err != nil {
|
| 21 |
- return err |
|
| 21 |
+ if cgroups.IsNotFound(err) {
|
|
| 22 |
+ return nil |
|
| 23 |
+ } else {
|
|
| 24 |
+ return err |
|
| 25 |
+ } |
|
| 22 | 26 |
} |
| 23 | 27 |
|
| 24 | 28 |
if err := s.Set(dir, d.c); err != nil {
|
| ... | ... |
@@ -17,7 +17,11 @@ type CpusetGroup struct {
|
| 17 | 17 |
func (s *CpusetGroup) Apply(d *data) error {
|
| 18 | 18 |
dir, err := d.path("cpuset")
|
| 19 | 19 |
if err != nil {
|
| 20 |
- return err |
|
| 20 |
+ if cgroups.IsNotFound(err) {
|
|
| 21 |
+ return nil |
|
| 22 |
+ } else {
|
|
| 23 |
+ return err |
|
| 24 |
+ } |
|
| 21 | 25 |
} |
| 22 | 26 |
return s.ApplyDir(dir, d.c, d.pid) |
| 23 | 27 |
} |
| ... | ... |
@@ -11,7 +11,11 @@ type DevicesGroup struct {
|
| 11 | 11 |
func (s *DevicesGroup) Apply(d *data) error {
|
| 12 | 12 |
dir, err := d.join("devices")
|
| 13 | 13 |
if err != nil {
|
| 14 |
- return err |
|
| 14 |
+ if cgroups.IsNotFound(err) {
|
|
| 15 |
+ return nil |
|
| 16 |
+ } else {
|
|
| 17 |
+ return err |
|
| 18 |
+ } |
|
| 15 | 19 |
} |
| 16 | 20 |
|
| 17 | 21 |
if err := s.Set(dir, d.c); err != nil {
|
| ... | ... |
@@ -25,7 +25,7 @@ func TestDevicesSetAllow(t *testing.T) {
|
| 25 | 25 |
defer helper.cleanup() |
| 26 | 26 |
|
| 27 | 27 |
helper.writeFileContents(map[string]string{
|
| 28 |
- "device.deny": "a", |
|
| 28 |
+ "devices.deny": "a", |
|
| 29 | 29 |
}) |
| 30 | 30 |
|
| 31 | 31 |
helper.CgroupData.c.AllowAllDevices = false |
| ... | ... |
@@ -35,8 +35,6 @@ func TestDevicesSetAllow(t *testing.T) {
|
| 35 | 35 |
t.Fatal(err) |
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
- // FIXME: this doesn't make sence, the file devices.allow under real cgroupfs |
|
| 39 |
- // is not allowed to read. Our test path don't have cgroupfs mounted. |
|
| 40 | 38 |
value, err := getCgroupParamString(helper.CgroupPath, "devices.allow") |
| 41 | 39 |
if err != nil {
|
| 42 | 40 |
t.Fatalf("Failed to parse devices.allow - %s", err)
|
| ... | ... |
@@ -13,8 +13,12 @@ type FreezerGroup struct {
|
| 13 | 13 |
|
| 14 | 14 |
func (s *FreezerGroup) Apply(d *data) error {
|
| 15 | 15 |
dir, err := d.join("freezer")
|
| 16 |
- if err != nil && !cgroups.IsNotFound(err) {
|
|
| 17 |
- return err |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ if cgroups.IsNotFound(err) {
|
|
| 18 |
+ return nil |
|
| 19 |
+ } else {
|
|
| 20 |
+ return err |
|
| 21 |
+ } |
|
| 18 | 22 |
} |
| 19 | 23 |
|
| 20 | 24 |
if err := s.Set(dir, d.c); err != nil {
|
| ... | ... |
@@ -16,9 +16,12 @@ type MemoryGroup struct {
|
| 16 | 16 |
|
| 17 | 17 |
func (s *MemoryGroup) Apply(d *data) error {
|
| 18 | 18 |
dir, err := d.join("memory")
|
| 19 |
- // only return an error for memory if it was specified |
|
| 20 |
- if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) {
|
|
| 21 |
- return err |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ if cgroups.IsNotFound(err) {
|
|
| 21 |
+ return nil |
|
| 22 |
+ } else {
|
|
| 23 |
+ return err |
|
| 24 |
+ } |
|
| 22 | 25 |
} |
| 23 | 26 |
defer func() {
|
| 24 | 27 |
if err != nil {
|
| ... | ... |
@@ -55,6 +55,63 @@ func TestMemorySetMemory(t *testing.T) {
|
| 55 | 55 |
} |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
+func TestMemorySetMemoryswap(t *testing.T) {
|
|
| 59 |
+ helper := NewCgroupTestUtil("memory", t)
|
|
| 60 |
+ defer helper.cleanup() |
|
| 61 |
+ |
|
| 62 |
+ const ( |
|
| 63 |
+ memoryswapBefore = 314572800 // 300M |
|
| 64 |
+ memoryswapAfter = 524288000 // 500M |
|
| 65 |
+ ) |
|
| 66 |
+ |
|
| 67 |
+ helper.writeFileContents(map[string]string{
|
|
| 68 |
+ "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore), |
|
| 69 |
+ }) |
|
| 70 |
+ |
|
| 71 |
+ helper.CgroupData.c.MemorySwap = memoryswapAfter |
|
| 72 |
+ memory := &MemoryGroup{}
|
|
| 73 |
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
|
|
| 74 |
+ t.Fatal(err) |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err)
|
|
| 80 |
+ } |
|
| 81 |
+ if value != memoryswapAfter {
|
|
| 82 |
+ t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
|
|
| 83 |
+ } |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+func TestMemorySetMemoryswapDefault(t *testing.T) {
|
|
| 87 |
+ helper := NewCgroupTestUtil("memory", t)
|
|
| 88 |
+ defer helper.cleanup() |
|
| 89 |
+ |
|
| 90 |
+ const ( |
|
| 91 |
+ memoryBefore = 209715200 // 200M |
|
| 92 |
+ memoryAfter = 314572800 // 300M |
|
| 93 |
+ memoryswapAfter = 629145600 // 300M*2 |
|
| 94 |
+ ) |
|
| 95 |
+ |
|
| 96 |
+ helper.writeFileContents(map[string]string{
|
|
| 97 |
+ "memory.limit_in_bytes": strconv.Itoa(memoryBefore), |
|
| 98 |
+ }) |
|
| 99 |
+ |
|
| 100 |
+ helper.CgroupData.c.Memory = memoryAfter |
|
| 101 |
+ memory := &MemoryGroup{}
|
|
| 102 |
+ if err := memory.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
|
|
| 103 |
+ t.Fatal(err) |
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ value, err := getCgroupParamUint(helper.CgroupPath, "memory.memsw.limit_in_bytes") |
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ t.Fatalf("Failed to parse memory.memsw.limit_in_bytes - %s", err)
|
|
| 109 |
+ } |
|
| 110 |
+ if value != memoryswapAfter {
|
|
| 111 |
+ t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
|
|
| 112 |
+ } |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 58 | 115 |
func TestMemoryStats(t *testing.T) {
|
| 59 | 116 |
helper := NewCgroupTestUtil("memory", t)
|
| 60 | 117 |
defer helper.cleanup() |
| ... | ... |
@@ -6,9 +6,9 @@ Creates a mock of the cgroup filesystem for the duration of the test. |
| 6 | 6 |
package fs |
| 7 | 7 |
|
| 8 | 8 |
import ( |
| 9 |
- "fmt" |
|
| 10 | 9 |
"io/ioutil" |
| 11 | 10 |
"os" |
| 11 |
+ "path/filepath" |
|
| 12 | 12 |
"testing" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/docker/libcontainer/configs" |
| ... | ... |
@@ -31,12 +31,12 @@ func NewCgroupTestUtil(subsystem string, t *testing.T) *cgroupTestUtil {
|
| 31 | 31 |
d := &data{
|
| 32 | 32 |
c: &configs.Cgroup{},
|
| 33 | 33 |
} |
| 34 |
- tempDir, err := ioutil.TempDir("", fmt.Sprintf("%s_cgroup_test", subsystem))
|
|
| 34 |
+ tempDir, err := ioutil.TempDir("", "cgroup_test")
|
|
| 35 | 35 |
if err != nil {
|
| 36 | 36 |
t.Fatal(err) |
| 37 | 37 |
} |
| 38 | 38 |
d.root = tempDir |
| 39 |
- testCgroupPath, err := d.path(subsystem) |
|
| 39 |
+ testCgroupPath := filepath.Join(d.root, subsystem) |
|
| 40 | 40 |
if err != nil {
|
| 41 | 41 |
t.Fatal(err) |
| 42 | 42 |
} |
| ... | ... |
@@ -34,6 +34,21 @@ func FindCgroupMountpoint(subsystem string) (string, error) {
|
| 34 | 34 |
return "", NewNotFoundError(subsystem) |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
+func FindCgroupMountpointDir() (string, error) {
|
|
| 38 |
+ mounts, err := mount.GetMounts() |
|
| 39 |
+ if err != nil {
|
|
| 40 |
+ return "", err |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ for _, mount := range mounts {
|
|
| 44 |
+ if mount.Fstype == "cgroup" {
|
|
| 45 |
+ return filepath.Dir(mount.Mountpoint), nil |
|
| 46 |
+ } |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ return "", NewNotFoundError("cgroup")
|
|
| 50 |
+} |
|
| 51 |
+ |
|
| 37 | 52 |
type Mount struct {
|
| 38 | 53 |
Mountpoint string |
| 39 | 54 |
Subsystems []string |
| ... | ... |
@@ -1,25 +1,12 @@ |
| 1 |
-// +build linux |
|
| 1 |
+// +build linux,!gccgo |
|
| 2 | 2 |
|
| 3 | 3 |
package nsenter |
| 4 | 4 |
|
| 5 | 5 |
/* |
| 6 | 6 |
#cgo CFLAGS: -Wall |
| 7 | 7 |
extern void nsexec(); |
| 8 |
-void __attribute__((constructor)) init() {
|
|
| 8 |
+void __attribute__((constructor)) init(void) {
|
|
| 9 | 9 |
nsexec(); |
| 10 | 10 |
} |
| 11 | 11 |
*/ |
| 12 | 12 |
import "C" |
| 13 |
- |
|
| 14 |
-// AlwaysFalse is here to stay false |
|
| 15 |
-// (and be exported so the compiler doesn't optimize out its reference) |
|
| 16 |
-var AlwaysFalse bool |
|
| 17 |
- |
|
| 18 |
-func init() {
|
|
| 19 |
- if AlwaysFalse {
|
|
| 20 |
- // by referencing this C init() in a noop test, it will ensure the compiler |
|
| 21 |
- // links in the C function. |
|
| 22 |
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65134 |
|
| 23 |
- C.init() |
|
| 24 |
- } |
|
| 25 |
-} |
| 26 | 13 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+// +build linux,gccgo |
|
| 1 |
+ |
|
| 2 |
+package nsenter |
|
| 3 |
+ |
|
| 4 |
+/* |
|
| 5 |
+#cgo CFLAGS: -Wall |
|
| 6 |
+extern void nsexec(); |
|
| 7 |
+void __attribute__((constructor)) init(void) {
|
|
| 8 |
+ nsexec(); |
|
| 9 |
+} |
|
| 10 |
+*/ |
|
| 11 |
+import "C" |
|
| 12 |
+ |
|
| 13 |
+// AlwaysFalse is here to stay false |
|
| 14 |
+// (and be exported so the compiler doesn't optimize out its reference) |
|
| 15 |
+var AlwaysFalse bool |
|
| 16 |
+ |
|
| 17 |
+func init() {
|
|
| 18 |
+ if AlwaysFalse {
|
|
| 19 |
+ // by referencing this C init() in a noop test, it will ensure the compiler |
|
| 20 |
+ // links in the C function. |
|
| 21 |
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65134 |
|
| 22 |
+ C.init() |
|
| 23 |
+ } |
|
| 24 |
+} |
| ... | ... |
@@ -104,6 +104,10 @@ func mount(m *configs.Mount, rootfs, mountLabel string) error {
|
| 104 | 104 |
if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
|
| 105 | 105 |
return err |
| 106 | 106 |
} |
| 107 |
+ if m.Device == "mqueue" {
|
|
| 108 |
+ // mqueue should not be labeled, otherwise the mount will fail |
|
| 109 |
+ data = "" |
|
| 110 |
+ } |
|
| 107 | 111 |
return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) |
| 108 | 112 |
case "bind": |
| 109 | 113 |
stat, err := os.Stat(m.Source) |
| ... | ... |
@@ -37,8 +37,8 @@ var ( |
| 37 | 37 |
spaceRegex = regexp.MustCompile(`^([^=]+) (.*)$`) |
| 38 | 38 |
mcsList = make(map[string]bool) |
| 39 | 39 |
selinuxfs = "unknown" |
| 40 |
- selinuxEnabled = false |
|
| 41 |
- selinuxEnabledChecked = false |
|
| 40 |
+ selinuxEnabled = false // Stores whether selinux is currently enabled |
|
| 41 |
+ selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet |
|
| 42 | 42 |
) |
| 43 | 43 |
|
| 44 | 44 |
type SELinuxContext map[string]string |
| ... | ... |
@@ -48,6 +48,11 @@ func SetDisabled() {
|
| 48 | 48 |
selinuxEnabled, selinuxEnabledChecked = false, true |
| 49 | 49 |
} |
| 50 | 50 |
|
| 51 |
+// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs |
|
| 52 |
+// filesystem or an empty string if no mountpoint is found. Selinuxfs is |
|
| 53 |
+// a proc-like pseudo-filesystem that exposes the selinux policy API to |
|
| 54 |
+// processes. The existence of an selinuxfs mount is used to determine |
|
| 55 |
+// whether selinux is currently enabled or not. |
|
| 51 | 56 |
func getSelinuxMountPoint() string {
|
| 52 | 57 |
if selinuxfs != "unknown" {
|
| 53 | 58 |
return selinuxfs |
| ... | ... |
@@ -74,6 +79,7 @@ func getSelinuxMountPoint() string {
|
| 74 | 74 |
return selinuxfs |
| 75 | 75 |
} |
| 76 | 76 |
|
| 77 |
+// SelinuxEnabled returns whether selinux is currently enabled. |
|
| 77 | 78 |
func SelinuxEnabled() bool {
|
| 78 | 79 |
if selinuxEnabledChecked {
|
| 79 | 80 |
return selinuxEnabled |
| ... | ... |
@@ -145,11 +151,12 @@ func readCon(name string) (string, error) {
|
| 145 | 145 |
return val, err |
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
+// Setfilecon sets the SELinux label for this path or returns an error. |
|
| 148 | 149 |
func Setfilecon(path string, scon string) error {
|
| 149 | 150 |
return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) |
| 150 | 151 |
} |
| 151 | 152 |
|
| 152 |
-// Return the SELinux label for this path |
|
| 153 |
+// Getfilecon returns the SELinux label for this path or returns an error. |
|
| 153 | 154 |
func Getfilecon(path string) (string, error) {
|
| 154 | 155 |
con, err := system.Lgetxattr(path, xattrNameSelinux) |
| 155 | 156 |
return string(con), err |
| ... | ... |
@@ -163,11 +170,12 @@ func Getfscreatecon() (string, error) {
|
| 163 | 163 |
return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
|
| 164 | 164 |
} |
| 165 | 165 |
|
| 166 |
-// Return the SELinux label of the current process thread. |
|
| 166 |
+// Getcon returns the SELinux label of the current process thread, or an error. |
|
| 167 | 167 |
func Getcon() (string, error) {
|
| 168 | 168 |
return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
|
| 169 | 169 |
} |
| 170 | 170 |
|
| 171 |
+// Getpidcon returns the SELinux label of the given pid, or an error. |
|
| 171 | 172 |
func Getpidcon(pid int) (string, error) {
|
| 172 | 173 |
return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
|
| 173 | 174 |
} |