Browse Source

Update libcontainer and make it the source of truth on logrus version

To help avoid version mismatches between libcontainer and Docker, this updates libcontainer to be the source of truth for which version of logrus the project is using.  This should help avoid potential incompatibilities in the future, too. :+1:

Signed-off-by: Andrew "Tianon" Page <admwiggin@gmail.com>
Tianon Gravi 10 years ago
parent
commit
80a895142e
58 changed files with 1154 additions and 401 deletions
  1. 1 1
      daemon/execdriver/native/init.go
  2. 2 4
      hack/vendor.sh
  3. 4 0
      vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md
  4. 21 24
      vendor/src/github.com/Sirupsen/logrus/README.md
  5. 4 0
      vendor/src/github.com/Sirupsen/logrus/formatter.go
  6. 10 2
      vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
  7. 10 3
      vendor/src/github.com/Sirupsen/logrus/json_formatter.go
  8. 4 5
      vendor/src/github.com/Sirupsen/logrus/text_formatter.go
  9. 4 2
      vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go
  10. 36 13
      vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go
  11. 26 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go
  12. 124 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go
  13. 11 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go
  14. 37 1
      vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go
  15. 29 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go
  16. 1 0
      vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go
  17. 1 1
      vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go
  18. 1 1
      vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go
  19. 2 0
      vendor/src/github.com/docker/libcontainer/cgroups/stats.go
  20. 0 4
      vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go
  21. 64 14
      vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go
  22. 17 0
      vendor/src/github.com/docker/libcontainer/configs/cgroup.go
  23. 7 0
      vendor/src/github.com/docker/libcontainer/configs/config.go
  24. 13 0
      vendor/src/github.com/docker/libcontainer/configs/mount.go
  25. 1 30
      vendor/src/github.com/docker/libcontainer/configs/namespaces.go
  26. 31 0
      vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go
  27. 15 0
      vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go
  28. 2 2
      vendor/src/github.com/docker/libcontainer/configs/network.go
  29. 1 1
      vendor/src/github.com/docker/libcontainer/console_linux.go
  30. 1 1
      vendor/src/github.com/docker/libcontainer/container.go
  31. 12 10
      vendor/src/github.com/docker/libcontainer/container_linux.go
  32. 1 1
      vendor/src/github.com/docker/libcontainer/devices/devices.go
  33. 5 7
      vendor/src/github.com/docker/libcontainer/factory.go
  34. 6 1
      vendor/src/github.com/docker/libcontainer/factory_linux.go
  35. 11 10
      vendor/src/github.com/docker/libcontainer/init_linux.go
  36. 270 155
      vendor/src/github.com/docker/libcontainer/integration/exec_test.go
  37. 108 84
      vendor/src/github.com/docker/libcontainer/integration/execin_test.go
  38. 1 1
      vendor/src/github.com/docker/libcontainer/integration/init_test.go
  39. 11 0
      vendor/src/github.com/docker/libcontainer/integration/utils_test.go
  40. 13 1
      vendor/src/github.com/docker/libcontainer/label/label_selinux.go
  41. 28 0
      vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go
  42. 23 4
      vendor/src/github.com/docker/libcontainer/nsenter/README.md
  43. 1 1
      vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go
  44. 21 2
      vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c
  45. 45 0
      vendor/src/github.com/docker/libcontainer/nsinit/README.md
  46. 7 0
      vendor/src/github.com/docker/libcontainer/nsinit/config.go
  47. 1 0
      vendor/src/github.com/docker/libcontainer/nsinit/exec.go
  48. 1 1
      vendor/src/github.com/docker/libcontainer/nsinit/init.go
  49. 1 2
      vendor/src/github.com/docker/libcontainer/nsinit/oom.go
  50. 1 2
      vendor/src/github.com/docker/libcontainer/nsinit/pause.go
  51. 11 1
      vendor/src/github.com/docker/libcontainer/nsinit/utils.go
  52. 5 2
      vendor/src/github.com/docker/libcontainer/process.go
  53. 3 0
      vendor/src/github.com/docker/libcontainer/process_linux.go
  54. 76 4
      vendor/src/github.com/docker/libcontainer/rootfs_linux.go
  55. 7 0
      vendor/src/github.com/docker/libcontainer/standard_init_linux.go
  56. 3 1
      vendor/src/github.com/docker/libcontainer/system/setns_linux.go
  57. 1 1
      vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go
  58. 1 1
      vendor/src/github.com/docker/libcontainer/update-vendor.sh

+ 1 - 1
daemon/execdriver/native/init.go

@@ -32,7 +32,7 @@ func initializer() {
 	if err != nil {
 	if err != nil {
 		fatal(err)
 		fatal(err)
 	}
 	}
-	if err := factory.StartInitialization(3); err != nil {
+	if err := factory.StartInitialization(); err != nil {
 		fatal(err)
 		fatal(err)
 	}
 	}
 
 

+ 2 - 4
hack/vendor.sh

@@ -53,8 +53,6 @@ clone hg code.google.com/p/gosqlite 74691fb6f837
 
 
 clone git github.com/docker/libtrust 230dfd18c232
 clone git github.com/docker/libtrust 230dfd18c232
 
 
-clone git github.com/Sirupsen/logrus v0.7.2
-
 clone git github.com/go-fsnotify/fsnotify v1.2.0
 clone git github.com/go-fsnotify/fsnotify v1.2.0
 
 
 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673
 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673
@@ -69,8 +67,8 @@ mv tmp-digest src/github.com/docker/distribution/digest
 mkdir -p src/github.com/docker/distribution/registry
 mkdir -p src/github.com/docker/distribution/registry
 mv tmp-api src/github.com/docker/distribution/registry/api
 mv tmp-api src/github.com/docker/distribution/registry/api
 
 
-clone git github.com/docker/libcontainer bd8ec36106086f72b66e1be85a81202b93503e44
+clone git github.com/docker/libcontainer 6607689b1d06743003a45a722d9fe0bef36b274e
 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
 # 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
 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' | grep -v 'github.com/Sirupsen/logrus')"
+eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
 # we exclude "github.com/codegangsta/cli" here because it's only needed for "nsinit", which Docker doesn't include
 # we exclude "github.com/codegangsta/cli" here because it's only needed for "nsinit", which Docker doesn't include

+ 4 - 0
vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md

@@ -1,3 +1,7 @@
+# 0.7.3
+
+formatter/\*: allow configuration of timestamp layout
+
 # 0.7.2
 # 0.7.2
 
 
 formatter/text: Add configuration option for time format (#158)
 formatter/text: Add configuration option for time format (#158)

+ 21 - 24
vendor/src/github.com/Sirupsen/logrus/README.md

@@ -108,6 +108,16 @@ func main() {
     "omg":    true,
     "omg":    true,
     "number": 100,
     "number": 100,
   }).Fatal("The ice breaks!")
   }).Fatal("The ice breaks!")
+
+  // A common pattern is to re-use fields between logging statements by re-using
+  // the logrus.Entry returned from WithFields()
+  contextLogger := log.WithFields(log.Fields{
+    "common": "this is a common field",
+    "other": "I also should be logged always",
+  })
+
+  contextLogger.Info("I'll be logged with common and other field")
+  contextLogger.Info("Me too")
 }
 }
 ```
 ```
 
 
@@ -189,31 +199,18 @@ func init() {
 }
 }
 ```
 ```
 
 
-* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
-  Send errors to an exception tracking service compatible with the Airbrake API.
-  Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
-
-* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
-  Send errors to the Papertrail hosted logging service via UDP.
-
-* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
-  Send errors to remote syslog server.
-  Uses standard library `log/syslog` behind the scenes.
-
-* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go)
-  Send errors to the Bugsnag exception tracking service.
-
-* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
-  Send errors to a channel in hipchat.
-
-* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
-  Send logs to Loggly (https://www.loggly.com/)
-
-* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus)
-  Hook for Slack chat.
 
 
-* [`github.com/wercker/journalhook`](https://github.com/wercker/journalhook).
-  Hook for logging to `systemd-journald`.
+| Hook  | Description |
+| ----- | ----------- |
+| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
+| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. |
+| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
+| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
+| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
+| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
+| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
+| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
+| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) |
 
 
 #### Level logging
 #### Level logging
 
 

+ 4 - 0
vendor/src/github.com/Sirupsen/logrus/formatter.go

@@ -1,5 +1,9 @@
 package logrus
 package logrus
 
 
+import "time"
+
+const DefaultTimestampFormat = time.RFC3339
+
 // The Formatter interface is used to implement a custom Formatter. It takes an
 // The Formatter interface is used to implement a custom Formatter. It takes an
 // `Entry`. It exposes all the fields, including the default ones:
 // `Entry`. It exposes all the fields, including the default ones:
 //
 //

+ 10 - 2
vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go

@@ -3,19 +3,27 @@ package logstash
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
+
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
-	"time"
 )
 )
 
 
 // Formatter generates json in logstash format.
 // Formatter generates json in logstash format.
 // Logstash site: http://logstash.net/
 // Logstash site: http://logstash.net/
 type LogstashFormatter struct {
 type LogstashFormatter struct {
 	Type string // if not empty use for logstash type field.
 	Type string // if not empty use for logstash type field.
+
+	// TimestampFormat sets the format used for timestamps.
+	TimestampFormat string
 }
 }
 
 
 func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 	entry.Data["@version"] = 1
 	entry.Data["@version"] = 1
-	entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339)
+
+	if f.TimestampFormat == "" {
+		f.TimestampFormat = logrus.DefaultTimestampFormat
+	}
+
+	entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat)
 
 
 	// set message field
 	// set message field
 	v, ok := entry.Data["message"]
 	v, ok := entry.Data["message"]

+ 10 - 3
vendor/src/github.com/Sirupsen/logrus/json_formatter.go

@@ -3,10 +3,12 @@ package logrus
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"time"
 )
 )
 
 
-type JSONFormatter struct{}
+type JSONFormatter struct {
+	// TimestampFormat sets the format used for marshaling timestamps.
+	TimestampFormat string
+}
 
 
 func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 	data := make(Fields, len(entry.Data)+3)
 	data := make(Fields, len(entry.Data)+3)
@@ -21,7 +23,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 		}
 		}
 	}
 	}
 	prefixFieldClashes(data)
 	prefixFieldClashes(data)
-	data["time"] = entry.Time.Format(time.RFC3339)
+
+	if f.TimestampFormat == "" {
+		f.TimestampFormat = DefaultTimestampFormat
+	}
+
+	data["time"] = entry.Time.Format(f.TimestampFormat)
 	data["msg"] = entry.Message
 	data["msg"] = entry.Message
 	data["level"] = entry.Level.String()
 	data["level"] = entry.Level.String()
 
 

+ 4 - 5
vendor/src/github.com/Sirupsen/logrus/text_formatter.go

@@ -18,9 +18,8 @@ const (
 )
 )
 
 
 var (
 var (
-	baseTimestamp          time.Time
-	isTerminal             bool
-	defaultTimestampFormat = time.RFC3339
+	baseTimestamp time.Time
+	isTerminal    bool
 )
 )
 
 
 func init() {
 func init() {
@@ -47,7 +46,7 @@ type TextFormatter struct {
 	// the time passed since beginning of execution.
 	// the time passed since beginning of execution.
 	FullTimestamp bool
 	FullTimestamp bool
 
 
-	// Timestamp format to use for display, if a full timestamp is printed
+	// TimestampFormat to use for display when a full timestamp is printed
 	TimestampFormat string
 	TimestampFormat string
 
 
 	// The fields are sorted by default for a consistent output. For applications
 	// The fields are sorted by default for a consistent output. For applications
@@ -73,7 +72,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 	isColored := (f.ForceColors || isTerminal) && !f.DisableColors
 	isColored := (f.ForceColors || isTerminal) && !f.DisableColors
 
 
 	if f.TimestampFormat == "" {
 	if f.TimestampFormat == "" {
-		f.TimestampFormat = defaultTimestampFormat
+		f.TimestampFormat = DefaultTimestampFormat
 	}
 	}
 	if isColored {
 	if isColored {
 		f.printColored(b, entry, keys)
 		f.printColored(b, entry, keys)

+ 4 - 2
vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go

@@ -14,8 +14,10 @@ import (
 
 
 func IsEnabled() bool {
 func IsEnabled() bool {
 	if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
 	if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
-		buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
-		return err == nil && len(buf) > 1 && buf[0] == 'Y'
+		if _, err = os.Stat("/sbin/apparmor_parser"); err == nil {
+			buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
+			return err == nil && len(buf) > 1 && buf[0] == 'Y'
+		}
 	}
 	}
 	return false
 	return false
 }
 }

+ 36 - 13
vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go

@@ -1,6 +1,8 @@
 package fs
 package fs
 
 
 import (
 import (
+	"fmt"
+	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
@@ -19,6 +21,7 @@ var (
 		"cpuset":     &CpusetGroup{},
 		"cpuset":     &CpusetGroup{},
 		"cpuacct":    &CpuacctGroup{},
 		"cpuacct":    &CpuacctGroup{},
 		"blkio":      &BlkioGroup{},
 		"blkio":      &BlkioGroup{},
+		"hugetlb":    &HugetlbGroup{},
 		"perf_event": &PerfEventGroup{},
 		"perf_event": &PerfEventGroup{},
 		"freezer":    &FreezerGroup{},
 		"freezer":    &FreezerGroup{},
 	}
 	}
@@ -75,10 +78,13 @@ type data struct {
 }
 }
 
 
 func (m *Manager) Apply(pid int) error {
 func (m *Manager) Apply(pid int) error {
+
 	if m.Cgroups == nil {
 	if m.Cgroups == nil {
 		return nil
 		return nil
 	}
 	}
 
 
+	var c = m.Cgroups
+
 	d, err := getCgroupData(m.Cgroups, pid)
 	d, err := getCgroupData(m.Cgroups, pid)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -108,6 +114,12 @@ func (m *Manager) Apply(pid int) error {
 	}
 	}
 	m.Paths = paths
 	m.Paths = paths
 
 
+	if paths["cpu"] != "" {
+		if err := CheckCpushares(paths["cpu"], c.CpuShares); err != nil {
+			return err
+		}
+	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -119,19 +131,6 @@ func (m *Manager) GetPaths() map[string]string {
 	return m.Paths
 	return m.Paths
 }
 }
 
 
-// Symmetrical public function to update device based cgroups.  Also available
-// in the systemd implementation.
-func ApplyDevices(c *configs.Cgroup, pid int) error {
-	d, err := getCgroupData(c, pid)
-	if err != nil {
-		return err
-	}
-
-	devices := subsystems["devices"]
-
-	return devices.Apply(d)
-}
-
 func (m *Manager) GetStats() (*cgroups.Stats, error) {
 func (m *Manager) GetStats() (*cgroups.Stats, error) {
 	stats := cgroups.NewStats()
 	stats := cgroups.NewStats()
 	for name, path := range m.Paths {
 	for name, path := range m.Paths {
@@ -280,3 +279,27 @@ func removePath(p string, err error) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+func CheckCpushares(path string, c int64) error {
+	var cpuShares int64
+
+	fd, err := os.Open(filepath.Join(path, "cpu.shares"))
+	if err != nil {
+		return err
+	}
+	defer fd.Close()
+
+	_, err = fmt.Fscanf(fd, "%d", &cpuShares)
+	if err != nil && err != io.EOF {
+		return err
+	}
+	if c != 0 {
+		if c > cpuShares {
+			return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares)
+		} else if c < cpuShares {
+			return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares)
+		}
+	}
+
+	return nil
+}

+ 26 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go

@@ -35,6 +35,32 @@ func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error {
 		}
 		}
 	}
 	}
 
 
+	if cgroup.BlkioWeightDevice != "" {
+		if err := writeFile(path, "blkio.weight_device", cgroup.BlkioWeightDevice); err != nil {
+			return err
+		}
+	}
+	if cgroup.BlkioThrottleReadBpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.read_bps_device", cgroup.BlkioThrottleReadBpsDevice); err != nil {
+			return err
+		}
+	}
+	if cgroup.BlkioThrottleWriteBpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.write_bps_device", cgroup.BlkioThrottleWriteBpsDevice); err != nil {
+			return err
+		}
+	}
+	if cgroup.BlkioThrottleReadIOpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.read_iops_device", cgroup.BlkioThrottleReadIOpsDevice); err != nil {
+			return err
+		}
+	}
+	if cgroup.BlkioThrottleWriteIOpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.write_iops_device", cgroup.BlkioThrottleWriteIOpsDevice); err != nil {
+			return err
+		}
+	}
+
 	return nil
 	return nil
 }
 }
 
 

+ 124 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go

@@ -67,6 +67,8 @@ Total 22061056`
 252:0 Async 164
 252:0 Async 164
 252:0 Total 164
 252:0 Total 164
 Total 328`
 Total 328`
+	throttleBefore = `8:0 1024`
+	throttleAfter  = `8:0 2048`
 )
 )
 
 
 func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) {
 func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) {
@@ -102,6 +104,35 @@ func TestBlkioSetWeight(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestBlkioSetWeightDevice(t *testing.T) {
+	helper := NewCgroupTestUtil("blkio", t)
+	defer helper.cleanup()
+
+	const (
+		weightDeviceBefore = "8:0 400"
+		weightDeviceAfter  = "8:0 500"
+	)
+
+	helper.writeFileContents(map[string]string{
+		"blkio.weight_device": weightDeviceBefore,
+	})
+
+	helper.CgroupData.c.BlkioWeightDevice = weightDeviceAfter
+	blkio := &BlkioGroup{}
+	if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device")
+	if err != nil {
+		t.Fatalf("Failed to parse blkio.weight_device - %s", err)
+	}
+
+	if value != weightDeviceAfter {
+		t.Fatal("Got the wrong value, set blkio.weight_device failed.")
+	}
+}
+
 func TestBlkioStats(t *testing.T) {
 func TestBlkioStats(t *testing.T) {
 	helper := NewCgroupTestUtil("blkio", t)
 	helper := NewCgroupTestUtil("blkio", t)
 	defer helper.cleanup()
 	defer helper.cleanup()
@@ -442,3 +473,96 @@ func TestNonCFQBlkioStats(t *testing.T) {
 
 
 	expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
 	expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats)
 }
 }
+
+func TestBlkioSetThrottleReadBpsDevice(t *testing.T) {
+	helper := NewCgroupTestUtil("blkio", t)
+	defer helper.cleanup()
+
+	helper.writeFileContents(map[string]string{
+		"blkio.throttle.read_bps_device": throttleBefore,
+	})
+
+	helper.CgroupData.c.BlkioThrottleReadBpsDevice = throttleAfter
+	blkio := &BlkioGroup{}
+	if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_bps_device")
+	if err != nil {
+		t.Fatalf("Failed to parse blkio.throttle.read_bps_device - %s", err)
+	}
+
+	if value != throttleAfter {
+		t.Fatal("Got the wrong value, set blkio.throttle.read_bps_device failed.")
+	}
+}
+func TestBlkioSetThrottleWriteBpsDevice(t *testing.T) {
+	helper := NewCgroupTestUtil("blkio", t)
+	defer helper.cleanup()
+
+	helper.writeFileContents(map[string]string{
+		"blkio.throttle.write_bps_device": throttleBefore,
+	})
+
+	helper.CgroupData.c.BlkioThrottleWriteBpsDevice = throttleAfter
+	blkio := &BlkioGroup{}
+	if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_bps_device")
+	if err != nil {
+		t.Fatalf("Failed to parse blkio.throttle.write_bps_device - %s", err)
+	}
+
+	if value != throttleAfter {
+		t.Fatal("Got the wrong value, set blkio.throttle.write_bps_device failed.")
+	}
+}
+func TestBlkioSetThrottleReadIOpsDevice(t *testing.T) {
+	helper := NewCgroupTestUtil("blkio", t)
+	defer helper.cleanup()
+
+	helper.writeFileContents(map[string]string{
+		"blkio.throttle.read_iops_device": throttleBefore,
+	})
+
+	helper.CgroupData.c.BlkioThrottleReadIOpsDevice = throttleAfter
+	blkio := &BlkioGroup{}
+	if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_iops_device")
+	if err != nil {
+		t.Fatalf("Failed to parse blkio.throttle.read_iops_device - %s", err)
+	}
+
+	if value != throttleAfter {
+		t.Fatal("Got the wrong value, set blkio.throttle.read_iops_device failed.")
+	}
+}
+func TestBlkioSetThrottleWriteIOpsDevice(t *testing.T) {
+	helper := NewCgroupTestUtil("blkio", t)
+	defer helper.cleanup()
+
+	helper.writeFileContents(map[string]string{
+		"blkio.throttle.write_iops_device": throttleBefore,
+	})
+
+	helper.CgroupData.c.BlkioThrottleWriteIOpsDevice = throttleAfter
+	blkio := &BlkioGroup{}
+	if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_iops_device")
+	if err != nil {
+		t.Fatalf("Failed to parse blkio.throttle.write_iops_device - %s", err)
+	}
+
+	if value != throttleAfter {
+		t.Fatal("Got the wrong value, set blkio.throttle.write_iops_device failed.")
+	}
+}

+ 11 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go

@@ -32,6 +32,17 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
 				return err
 				return err
 			}
 			}
 		}
 		}
+		return nil
+	}
+
+	if err := writeFile(path, "devices.allow", "a"); err != nil {
+		return err
+	}
+
+	for _, dev := range cgroup.DeniedDevices {
+		if err := writeFile(path, "devices.deny", dev.CgroupString()); err != nil {
+			return err
+		}
 	}
 	}
 
 
 	return nil
 	return nil

+ 37 - 1
vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go

@@ -17,7 +17,18 @@ var (
 			FileMode:    0666,
 			FileMode:    0666,
 		},
 		},
 	}
 	}
-	allowedList = "c 1:5 rwm"
+	allowedList   = "c 1:5 rwm"
+	deniedDevices = []*configs.Device{
+		{
+			Path:        "/dev/null",
+			Type:        'c',
+			Major:       1,
+			Minor:       3,
+			Permissions: "rwm",
+			FileMode:    0666,
+		},
+	}
+	deniedList = "c 1:3 rwm"
 )
 )
 
 
 func TestDevicesSetAllow(t *testing.T) {
 func TestDevicesSetAllow(t *testing.T) {
@@ -44,3 +55,28 @@ func TestDevicesSetAllow(t *testing.T) {
 		t.Fatal("Got the wrong value, set devices.allow failed.")
 		t.Fatal("Got the wrong value, set devices.allow failed.")
 	}
 	}
 }
 }
+
+func TestDevicesSetDeny(t *testing.T) {
+	helper := NewCgroupTestUtil("devices", t)
+	defer helper.cleanup()
+
+	helper.writeFileContents(map[string]string{
+		"devices.allow": "a",
+	})
+
+	helper.CgroupData.c.AllowAllDevices = true
+	helper.CgroupData.c.DeniedDevices = deniedDevices
+	devices := &DevicesGroup{}
+	if err := devices.Set(helper.CgroupPath, helper.CgroupData.c); err != nil {
+		t.Fatal(err)
+	}
+
+	value, err := getCgroupParamString(helper.CgroupPath, "devices.deny")
+	if err != nil {
+		t.Fatalf("Failed to parse devices.deny - %s", err)
+	}
+
+	if value != deniedList {
+		t.Fatal("Got the wrong value, set devices.deny failed.")
+	}
+}

+ 29 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go

@@ -0,0 +1,29 @@
+package fs
+
+import (
+	"github.com/docker/libcontainer/cgroups"
+	"github.com/docker/libcontainer/configs"
+)
+
+type HugetlbGroup struct {
+}
+
+func (s *HugetlbGroup) Apply(d *data) error {
+	// we just want to join this group even though we don't set anything
+	if _, err := d.join("hugetlb"); err != nil && !cgroups.IsNotFound(err) {
+		return err
+	}
+	return nil
+}
+
+func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error {
+	return nil
+}
+
+func (s *HugetlbGroup) Remove(d *data) error {
+	return removePath(d.path("hugetlb"))
+}
+
+func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error {
+	return nil
+}

+ 1 - 0
vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go

@@ -95,6 +95,7 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
 		return fmt.Errorf("failed to parse memory.usage_in_bytes - %v", err)
 		return fmt.Errorf("failed to parse memory.usage_in_bytes - %v", err)
 	}
 	}
 	stats.MemoryStats.Usage = value
 	stats.MemoryStats.Usage = value
+	stats.MemoryStats.Cache = stats.MemoryStats.Stats["cache"]
 	value, err = getCgroupParamUint(path, "memory.max_usage_in_bytes")
 	value, err = getCgroupParamUint(path, "memory.max_usage_in_bytes")
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("failed to parse memory.max_usage_in_bytes - %v", err)
 		return fmt.Errorf("failed to parse memory.max_usage_in_bytes - %v", err)

+ 1 - 1
vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go

@@ -128,7 +128,7 @@ func TestMemoryStats(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
+	expectedStats := cgroups.MemoryStats{Usage: 2048, Cache: 512, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}}
 	expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
 	expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
 }
 }
 
 

+ 1 - 1
vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go

@@ -2,9 +2,9 @@ package fs
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"log"
 	"testing"
 	"testing"
 
 
+	log "github.com/Sirupsen/logrus"
 	"github.com/docker/libcontainer/cgroups"
 	"github.com/docker/libcontainer/cgroups"
 )
 )
 
 

+ 2 - 0
vendor/src/github.com/docker/libcontainer/cgroups/stats.go

@@ -33,6 +33,8 @@ type CpuStats struct {
 type MemoryStats struct {
 type MemoryStats struct {
 	// current res_counter usage for memory
 	// current res_counter usage for memory
 	Usage uint64 `json:"usage,omitempty"`
 	Usage uint64 `json:"usage,omitempty"`
+	// memory used for cache
+	Cache uint64 `json:"cache,omitempty"`
 	// maximum usage ever recorded.
 	// maximum usage ever recorded.
 	MaxUsage uint64 `json:"max_usage,omitempty"`
 	MaxUsage uint64 `json:"max_usage,omitempty"`
 	// TODO(vishh): Export these as stronger types.
 	// TODO(vishh): Export these as stronger types.

+ 0 - 4
vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go

@@ -46,10 +46,6 @@ func (m *Manager) Freeze(state configs.FreezerState) error {
 	return fmt.Errorf("Systemd not supported")
 	return fmt.Errorf("Systemd not supported")
 }
 }
 
 
-func ApplyDevices(c *configs.Cgroup, pid int) error {
-	return fmt.Errorf("Systemd not supported")
-}
-
 func Freeze(c *configs.Cgroup, state configs.FreezerState) error {
 func Freeze(c *configs.Cgroup, state configs.FreezerState) error {
 	return fmt.Errorf("Systemd not supported")
 	return fmt.Errorf("Systemd not supported")
 }
 }

+ 64 - 14
vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go

@@ -38,6 +38,7 @@ var subsystems = map[string]subsystem{
 	"cpuset":     &fs.CpusetGroup{},
 	"cpuset":     &fs.CpusetGroup{},
 	"cpuacct":    &fs.CpuacctGroup{},
 	"cpuacct":    &fs.CpuacctGroup{},
 	"blkio":      &fs.BlkioGroup{},
 	"blkio":      &fs.BlkioGroup{},
+	"hugetlb":    &fs.HugetlbGroup{},
 	"perf_event": &fs.PerfEventGroup{},
 	"perf_event": &fs.PerfEventGroup{},
 	"freezer":    &fs.FreezerGroup{},
 	"freezer":    &fs.FreezerGroup{},
 }
 }
@@ -216,6 +217,13 @@ func (m *Manager) Apply(pid int) error {
 		return err
 		return err
 	}
 	}
 
 
+	// FIXME: Systemd does have `BlockIODeviceWeight` property, but we got problem
+	// using that (at least on systemd 208, see https://github.com/docker/libcontainer/pull/354),
+	// so use fs work around for now.
+	if err := joinBlkio(c, pid); err != nil {
+		return err
+	}
+
 	paths := make(map[string]string)
 	paths := make(map[string]string)
 	for sysname := range subsystems {
 	for sysname := range subsystems {
 		subsystemPath, err := getSubsystemPath(m.Cgroups, sysname)
 		subsystemPath, err := getSubsystemPath(m.Cgroups, sysname)
@@ -228,9 +236,14 @@ func (m *Manager) Apply(pid int) error {
 		}
 		}
 		paths[sysname] = subsystemPath
 		paths[sysname] = subsystemPath
 	}
 	}
-
 	m.Paths = paths
 	m.Paths = paths
 
 
+	if paths["cpu"] != "" {
+		if err := fs.CheckCpushares(paths["cpu"], c.CpuShares); err != nil {
+			return err
+		}
+	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -350,7 +363,17 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) {
 }
 }
 
 
 func (m *Manager) Set(container *configs.Config) error {
 func (m *Manager) Set(container *configs.Config) error {
-	panic("not implemented")
+	for name, path := range m.Paths {
+		sys, ok := subsystems[name]
+		if !ok || !cgroups.PathExists(path) {
+			continue
+		}
+		if err := sys.Set(path, container.Cgroups); err != nil {
+			return err
+		}
+	}
+
+	return nil
 }
 }
 
 
 func getUnitName(c *configs.Cgroup) string {
 func getUnitName(c *configs.Cgroup) string {
@@ -362,7 +385,7 @@ func getUnitName(c *configs.Cgroup) string {
 // * Support for wildcards to allow /dev/pts support
 // * Support for wildcards to allow /dev/pts support
 //
 //
 // The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
 // The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is
-// in wide use. When both these are availalable we will be able to switch, but need to keep the old
+// in wide use. When both these are available we will be able to switch, but need to keep the old
 // implementation for backwards compat.
 // implementation for backwards compat.
 //
 //
 // Note: we can't use systemd to set up the initial limits, and then change the cgroup
 // Note: we can't use systemd to set up the initial limits, and then change the cgroup
@@ -375,17 +398,7 @@ func joinDevices(c *configs.Cgroup, pid int) error {
 	}
 	}
 
 
 	devices := subsystems["devices"]
 	devices := subsystems["devices"]
-	if err := devices.Set(path, c); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Symmetrical public function to update device based cgroups.  Also available
-// in the fs implementation.
-func ApplyDevices(c *configs.Cgroup, pid int) error {
-	return joinDevices(c, pid)
+	return devices.Set(path, c)
 }
 }
 
 
 func joinMemory(c *configs.Cgroup, pid int) error {
 func joinMemory(c *configs.Cgroup, pid int) error {
@@ -417,3 +430,40 @@ func joinCpuset(c *configs.Cgroup, pid int) error {
 
 
 	return s.ApplyDir(path, c, pid)
 	return s.ApplyDir(path, c, pid)
 }
 }
+
+// `BlockIODeviceWeight` property of systemd does not work properly, and systemd
+// expects device path instead of major minor numbers, which is also confusing
+// for users. So we use fs work around for now.
+func joinBlkio(c *configs.Cgroup, pid int) error {
+	path, err := getSubsystemPath(c, "blkio")
+	if err != nil {
+		return err
+	}
+	if c.BlkioWeightDevice != "" {
+		if err := writeFile(path, "blkio.weight_device", c.BlkioWeightDevice); err != nil {
+			return err
+		}
+	}
+	if c.BlkioThrottleReadBpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.read_bps_device", c.BlkioThrottleReadBpsDevice); err != nil {
+			return err
+		}
+	}
+	if c.BlkioThrottleWriteBpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.write_bps_device", c.BlkioThrottleWriteBpsDevice); err != nil {
+			return err
+		}
+	}
+	if c.BlkioThrottleReadIOpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.read_iops_device", c.BlkioThrottleReadIOpsDevice); err != nil {
+			return err
+		}
+	}
+	if c.BlkioThrottleWriteIOpsDevice != "" {
+		if err := writeFile(path, "blkio.throttle.write_iops_device", c.BlkioThrottleWriteIOpsDevice); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 17 - 0
vendor/src/github.com/docker/libcontainer/configs/cgroup.go

@@ -19,6 +19,8 @@ type Cgroup struct {
 
 
 	AllowedDevices []*Device `json:"allowed_devices"`
 	AllowedDevices []*Device `json:"allowed_devices"`
 
 
+	DeniedDevices []*Device `json:"denied_devices"`
+
 	// Memory limit (in bytes)
 	// Memory limit (in bytes)
 	Memory int64 `json:"memory"`
 	Memory int64 `json:"memory"`
 
 
@@ -43,9 +45,24 @@ type Cgroup struct {
 	// MEM to use
 	// MEM to use
 	CpusetMems string `json:"cpuset_mems"`
 	CpusetMems string `json:"cpuset_mems"`
 
 
+	// IO read rate limit per cgroup per device, bytes per second.
+	BlkioThrottleReadBpsDevice string `json:"blkio_throttle_read_bps_device"`
+
+	// IO write rate limit per cgroup per divice, bytes per second.
+	BlkioThrottleWriteBpsDevice string `json:"blkio_throttle_write_bps_device"`
+
+	// IO read rate limit per cgroup per device, IO per second.
+	BlkioThrottleReadIOpsDevice string `json:"blkio_throttle_read_iops_device"`
+
+	// IO write rate limit per cgroup per device, IO per second.
+	BlkioThrottleWriteIOpsDevice string `json:"blkio_throttle_write_iops_device"`
+
 	// Specifies per cgroup weight, range is from 10 to 1000.
 	// Specifies per cgroup weight, range is from 10 to 1000.
 	BlkioWeight int64 `json:"blkio_weight"`
 	BlkioWeight int64 `json:"blkio_weight"`
 
 
+	// Weight per cgroup per device, can override BlkioWeight.
+	BlkioWeightDevice string `json:"blkio_weight_device"`
+
 	// set the freeze value for the process
 	// set the freeze value for the process
 	Freezer FreezerState `json:"freezer"`
 	Freezer FreezerState `json:"freezer"`
 
 

+ 7 - 0
vendor/src/github.com/docker/libcontainer/configs/config.go

@@ -37,6 +37,9 @@ type Config struct {
 	// bind mounts are writtable.
 	// bind mounts are writtable.
 	Readonlyfs bool `json:"readonlyfs"`
 	Readonlyfs bool `json:"readonlyfs"`
 
 
+	// Privatefs will mount the container's rootfs as private where mount points from the parent will not propogate
+	Privatefs bool `json:"privatefs"`
+
 	// Mounts specify additional source and destination paths that will be mounted inside the container's
 	// Mounts specify additional source and destination paths that will be mounted inside the container's
 	// rootfs and mount namespace if specified
 	// rootfs and mount namespace if specified
 	Mounts []*Mount `json:"mounts"`
 	Mounts []*Mount `json:"mounts"`
@@ -96,6 +99,10 @@ type Config struct {
 	// ReadonlyPaths specifies paths within the container's rootfs to remount as read-only
 	// ReadonlyPaths specifies paths within the container's rootfs to remount as read-only
 	// so that these files prevent any writes.
 	// so that these files prevent any writes.
 	ReadonlyPaths []string `json:"readonly_paths"`
 	ReadonlyPaths []string `json:"readonly_paths"`
+
+	// SystemProperties is a map of properties and their values. It is the equivalent of using
+	// sysctl -w my.property.name value in Linux.
+	SystemProperties map[string]string `json:"system_properties"`
 }
 }
 
 
 // Gets the root uid for the process on host which could be non-zero
 // Gets the root uid for the process on host which could be non-zero

+ 13 - 0
vendor/src/github.com/docker/libcontainer/configs/mount.go

@@ -18,4 +18,17 @@ type Mount struct {
 
 
 	// Relabel source if set, "z" indicates shared, "Z" indicates unshared.
 	// Relabel source if set, "z" indicates shared, "Z" indicates unshared.
 	Relabel string `json:"relabel"`
 	Relabel string `json:"relabel"`
+
+	// Optional Command to be run before Source is mounted.
+	PremountCmds []Command `json:"premount_cmds"`
+
+	// Optional Command to be run after Source is mounted.
+	PostmountCmds []Command `json:"postmount_cmds"`
+}
+
+type Command struct {
+	Path string   `json:"path"`
+	Args []string `json:"args"`
+	Env  []string `json:"env"`
+	Dir  string   `json:"dir"`
 }
 }

+ 1 - 30
vendor/src/github.com/docker/libcontainer/configs/namespaces.go

@@ -1,9 +1,6 @@
 package configs
 package configs
 
 
-import (
-	"fmt"
-	"syscall"
-)
+import "fmt"
 
 
 type NamespaceType string
 type NamespaceType string
 
 
@@ -34,10 +31,6 @@ type Namespace struct {
 	Path string        `json:"path"`
 	Path string        `json:"path"`
 }
 }
 
 
-func (n *Namespace) Syscall() int {
-	return namespaceInfo[n.Type]
-}
-
 func (n *Namespace) GetPath(pid int) string {
 func (n *Namespace) GetPath(pid int) string {
 	if n.Path != "" {
 	if n.Path != "" {
 		return n.Path
 		return n.Path
@@ -96,25 +89,3 @@ func (n *Namespaces) index(t NamespaceType) int {
 func (n *Namespaces) Contains(t NamespaceType) bool {
 func (n *Namespaces) Contains(t NamespaceType) bool {
 	return n.index(t) != -1
 	return n.index(t) != -1
 }
 }
-
-var namespaceInfo = map[NamespaceType]int{
-	NEWNET:  syscall.CLONE_NEWNET,
-	NEWNS:   syscall.CLONE_NEWNS,
-	NEWUSER: syscall.CLONE_NEWUSER,
-	NEWIPC:  syscall.CLONE_NEWIPC,
-	NEWUTS:  syscall.CLONE_NEWUTS,
-	NEWPID:  syscall.CLONE_NEWPID,
-}
-
-// CloneFlags parses the container's Namespaces options to set the correct
-// flags on clone, unshare. This functions returns flags only for new namespaces.
-func (n *Namespaces) CloneFlags() uintptr {
-	var flag int
-	for _, v := range *n {
-		if v.Path != "" {
-			continue
-		}
-		flag |= namespaceInfo[v.Type]
-	}
-	return uintptr(flag)
-}

+ 31 - 0
vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go

@@ -0,0 +1,31 @@
+// +build linux
+
+package configs
+
+import "syscall"
+
+func (n *Namespace) Syscall() int {
+	return namespaceInfo[n.Type]
+}
+
+var namespaceInfo = map[NamespaceType]int{
+	NEWNET:  syscall.CLONE_NEWNET,
+	NEWNS:   syscall.CLONE_NEWNS,
+	NEWUSER: syscall.CLONE_NEWUSER,
+	NEWIPC:  syscall.CLONE_NEWIPC,
+	NEWUTS:  syscall.CLONE_NEWUTS,
+	NEWPID:  syscall.CLONE_NEWPID,
+}
+
+// CloneFlags parses the container's Namespaces options to set the correct
+// flags on clone, unshare. This functions returns flags only for new namespaces.
+func (n *Namespaces) CloneFlags() uintptr {
+	var flag int
+	for _, v := range *n {
+		if v.Path != "" {
+			continue
+		}
+		flag |= namespaceInfo[v.Type]
+	}
+	return uintptr(flag)
+}

+ 15 - 0
vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go

@@ -0,0 +1,15 @@
+// +build !linux
+
+package configs
+
+func (n *Namespace) Syscall() int {
+	panic("No namespace syscall support")
+	return 0
+}
+
+// CloneFlags parses the container's Namespaces options to set the correct
+// flags on clone, unshare. This functions returns flags only for new namespaces.
+func (n *Namespaces) CloneFlags() uintptr {
+	panic("No namespace syscall support")
+	return uintptr(0)
+}

+ 2 - 2
vendor/src/github.com/docker/libcontainer/configs/network.go

@@ -2,7 +2,7 @@ package configs
 
 
 // Network defines configuration for a container's networking stack
 // Network defines configuration for a container's networking stack
 //
 //
-// The network configuration can be omited from a container causing the
+// The network configuration can be omitted from a container causing the
 // container to be setup with the host's networking stack
 // container to be setup with the host's networking stack
 type Network struct {
 type Network struct {
 	// Type sets the networks type, commonly veth and loopback
 	// Type sets the networks type, commonly veth and loopback
@@ -53,7 +53,7 @@ type Network struct {
 // Routes can be specified to create entries in the route table as the container is started
 // Routes can be specified to create entries in the route table as the container is started
 //
 //
 // All of destination, source, and gateway should be either IPv4 or IPv6.
 // All of destination, source, and gateway should be either IPv4 or IPv6.
-// One of the three options must be present, and ommitted entries will use their
+// One of the three options must be present, and omitted entries will use their
 // IP family default for the route table.  For IPv4 for example, setting the
 // IP family default for the route table.  For IPv4 for example, setting the
 // gateway to 1.2.3.4 and the interface to eth0 will set up a standard
 // gateway to 1.2.3.4 and the interface to eth0 will set up a standard
 // destination of 0.0.0.0(or *) when viewed in the route table.
 // destination of 0.0.0.0(or *) when viewed in the route table.

+ 1 - 1
vendor/src/github.com/docker/libcontainer/console_linux.go

@@ -38,7 +38,7 @@ func newConsole(uid, gid int) (Console, error) {
 	}, nil
 	}, nil
 }
 }
 
 
-// newConsoleFromPath is an internal fucntion returning an initialzied console for use inside
+// newConsoleFromPath is an internal function returning an initialized console for use inside
 // a container's MNT namespace.
 // a container's MNT namespace.
 func newConsoleFromPath(slavePath string) *linuxConsole {
 func newConsoleFromPath(slavePath string) *linuxConsole {
 	return &linuxConsole{
 	return &linuxConsole{

+ 1 - 1
vendor/src/github.com/docker/libcontainer/container.go

@@ -67,7 +67,7 @@ type Container interface {
 	// State returns the current container's state information.
 	// State returns the current container's state information.
 	//
 	//
 	// errors:
 	// errors:
-	// Systemerror - System erroor.
+	// Systemerror - System error.
 	State() (*State, error)
 	State() (*State, error)
 
 
 	// Returns the current config of the container.
 	// Returns the current config of the container.

+ 12 - 10
vendor/src/github.com/docker/libcontainer/container_linux.go

@@ -16,6 +16,8 @@ import (
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/configs"
 )
 )
 
 
+const stdioFdCount = 3
+
 type linuxContainer struct {
 type linuxContainer struct {
 	id            string
 	id            string
 	root          string
 	root          string
@@ -139,7 +141,8 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
 	if cmd.SysProcAttr == nil {
 	if cmd.SysProcAttr == nil {
 		cmd.SysProcAttr = &syscall.SysProcAttr{}
 		cmd.SysProcAttr = &syscall.SysProcAttr{}
 	}
 	}
-	cmd.ExtraFiles = []*os.File{childPipe}
+	cmd.ExtraFiles = append(p.ExtraFiles, childPipe)
+	cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
 	// NOTE: when running a container with no PID namespace and the parent process spawning the container is
 	// NOTE: when running a container with no PID namespace and the parent process spawning the container is
 	// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
 	// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
 	// even with the parent still running.
 	// even with the parent still running.
@@ -178,11 +181,9 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
 		fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
 		fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
 		"_LIBCONTAINER_INITTYPE=setns",
 		"_LIBCONTAINER_INITTYPE=setns",
 	)
 	)
-
 	if p.consolePath != "" {
 	if p.consolePath != "" {
 		cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath)
 		cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath)
 	}
 	}
-
 	// TODO: set on container for process management
 	// TODO: set on container for process management
 	return &setnsProcess{
 	return &setnsProcess{
 		cmd:         cmd,
 		cmd:         cmd,
@@ -195,13 +196,14 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
 
 
 func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
 func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
 	return &initConfig{
 	return &initConfig{
-		Config:       c.config,
-		Args:         process.Args,
-		Env:          process.Env,
-		User:         process.User,
-		Cwd:          process.Cwd,
-		Console:      process.consolePath,
-		Capabilities: process.Capabilities,
+		Config:           c.config,
+		Args:             process.Args,
+		Env:              process.Env,
+		User:             process.User,
+		Cwd:              process.Cwd,
+		Console:          process.consolePath,
+		Capabilities:     process.Capabilities,
+		PassedFilesCount: len(process.ExtraFiles),
 	}
 	}
 }
 }
 
 

+ 1 - 1
vendor/src/github.com/docker/libcontainer/devices/devices.go

@@ -21,7 +21,7 @@ var (
 	ioutilReadDir = ioutil.ReadDir
 	ioutilReadDir = ioutil.ReadDir
 )
 )
 
 
-// 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.
+// Given the path to a device and it's cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
 func DeviceFromPath(path, permissions string) (*configs.Device, error) {
 func DeviceFromPath(path, permissions string) (*configs.Device, error) {
 	fileInfo, err := osLstat(path)
 	fileInfo, err := osLstat(path)
 	if err != nil {
 	if err != nil {

+ 5 - 7
vendor/src/github.com/docker/libcontainer/factory.go

@@ -32,15 +32,13 @@ type Factory interface {
 	// System error
 	// System error
 	Load(id string) (Container, error)
 	Load(id string) (Container, error)
 
 
-	// StartInitialization is an internal API to libcontainer used during the rexec of the
-	// container.  pipefd is the fd to the child end of the pipe used to syncronize the
-	// parent and child process providing state and configuration to the child process and
-	// returning any errors during the init of the container
+	// StartInitialization is an internal API to libcontainer used during the reexec of the
+	// container.
 	//
 	//
 	// Errors:
 	// Errors:
-	// pipe connection error
-	// system error
-	StartInitialization(pipefd uintptr) error
+	// Pipe connection error
+	// System error
+	StartInitialization() error
 
 
 	// Type returns info string about factory type (e.g. lxc, libcontainer...)
 	// Type returns info string about factory type (e.g. lxc, libcontainer...)
 	Type() string
 	Type() string

+ 6 - 1
vendor/src/github.com/docker/libcontainer/factory_linux.go

@@ -10,6 +10,7 @@ import (
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"regexp"
 	"regexp"
+	"strconv"
 	"syscall"
 	"syscall"
 
 
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/mount"
@@ -194,7 +195,11 @@ func (l *LinuxFactory) Type() string {
 
 
 // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
 // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
 // This is a low level implementation detail of the reexec and should not be consumed externally
 // This is a low level implementation detail of the reexec and should not be consumed externally
-func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) {
+func (l *LinuxFactory) StartInitialization() (err error) {
+	pipefd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_INITPIPE"))
+	if err != nil {
+		return err
+	}
 	var (
 	var (
 		pipe = os.NewFile(uintptr(pipefd), "pipe")
 		pipe = os.NewFile(uintptr(pipefd), "pipe")
 		it   = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))
 		it   = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))

+ 11 - 10
vendor/src/github.com/docker/libcontainer/init_linux.go

@@ -40,14 +40,15 @@ type network struct {
 
 
 // initConfig is used for transferring parameters from Exec() to Init()
 // initConfig is used for transferring parameters from Exec() to Init()
 type initConfig struct {
 type initConfig struct {
-	Args         []string        `json:"args"`
-	Env          []string        `json:"env"`
-	Cwd          string          `json:"cwd"`
-	Capabilities []string        `json:"capabilities"`
-	User         string          `json:"user"`
-	Config       *configs.Config `json:"config"`
-	Console      string          `json:"console"`
-	Networks     []*network      `json:"network"`
+	Args             []string        `json:"args"`
+	Env              []string        `json:"env"`
+	Cwd              string          `json:"cwd"`
+	Capabilities     []string        `json:"capabilities"`
+	User             string          `json:"user"`
+	Config           *configs.Config `json:"config"`
+	Console          string          `json:"console"`
+	Networks         []*network      `json:"network"`
+	PassedFilesCount int             `json:"passed_files_count"`
 }
 }
 
 
 type initer interface {
 type initer interface {
@@ -95,10 +96,10 @@ func populateProcessEnvironment(env []string) error {
 // and working dir, and closes any leaked file descriptors
 // and working dir, and closes any leaked file descriptors
 // before executing the command inside the namespace
 // before executing the command inside the namespace
 func finalizeNamespace(config *initConfig) error {
 func finalizeNamespace(config *initConfig) error {
-	// Ensure that all non-standard fds we may have accidentally
+	// Ensure that all unwanted fds we may have accidentally
 	// inherited are marked close-on-exec so they stay out of the
 	// inherited are marked close-on-exec so they stay out of the
 	// container
 	// container
-	if err := utils.CloseExecFrom(3); err != nil {
+	if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 270 - 155
vendor/src/github.com/docker/libcontainer/integration/exec_test.go

@@ -4,8 +4,10 @@ import (
 	"bytes"
 	"bytes"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
+	"path/filepath"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+	"syscall"
 	"testing"
 	"testing"
 
 
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer"
@@ -29,9 +31,7 @@ func testExecPS(t *testing.T, userns bool) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	if userns {
 	if userns {
@@ -64,21 +64,15 @@ func TestIPCPrivate(t *testing.T) {
 	}
 	}
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	l, err := os.Readlink("/proc/1/ns/ipc")
 	l, err := os.Readlink("/proc/1/ns/ipc")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	if exitCode != 0 {
 	if exitCode != 0 {
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@@ -95,22 +89,16 @@ func TestIPCHost(t *testing.T) {
 	}
 	}
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	l, err := os.Readlink("/proc/1/ns/ipc")
 	l, err := os.Readlink("/proc/1/ns/ipc")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	config.Namespaces.Remove(configs.NEWIPC)
 	config.Namespaces.Remove(configs.NEWIPC)
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
 	buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	if exitCode != 0 {
 	if exitCode != 0 {
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@@ -127,23 +115,17 @@ func TestIPCJoinPath(t *testing.T) {
 	}
 	}
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	l, err := os.Readlink("/proc/1/ns/ipc")
 	l, err := os.Readlink("/proc/1/ns/ipc")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc")
 	config.Namespaces.Add(configs.NEWIPC, "/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 {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	if exitCode != 0 {
 	if exitCode != 0 {
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
 		t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr)
@@ -160,9 +142,7 @@ func TestIPCBadPath(t *testing.T) {
 	}
 	}
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
@@ -180,16 +160,12 @@ func TestRlimit(t *testing.T) {
 	}
 	}
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n")
 	out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n")
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" {
 	if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" {
 		t.Fatalf("expected rlimit to be 1025, got %s", limit)
 		t.Fatalf("expected rlimit to be 1025, got %s", limit)
 	}
 	}
@@ -208,9 +184,7 @@ func newTestRoot() (string, error) {
 
 
 func waitProcess(p *libcontainer.Process, t *testing.T) {
 func waitProcess(p *libcontainer.Process, t *testing.T) {
 	status, err := p.Wait()
 	status, err := p.Wait()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	if !status.Success() {
 	if !status.Success() {
 		t.Fatal(status)
 		t.Fatal(status)
 	}
 	}
@@ -221,35 +195,25 @@ func TestEnter(t *testing.T) {
 		return
 		return
 	}
 	}
 	root, err := newTestRoot()
 	root, err := newTestRoot()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer os.RemoveAll(root)
 	defer os.RemoveAll(root)
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 
 
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	container, err := factory.Create("test", config)
 	container, err := factory.Create("test", config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	// Execute a first process in the container
 	// Execute a first process in the container
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	var stdout, stdout2 bytes.Buffer
 	var stdout, stdout2 bytes.Buffer
 
 
@@ -262,19 +226,13 @@ func TestEnter(t *testing.T) {
 	err = container.Start(&pconfig)
 	err = container.Start(&pconfig)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	pid, err := pconfig.Pid()
 	pid, err := pconfig.Pid()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	// Execute another process in the container
 	// Execute another process in the container
 	stdinR2, stdinW2, err := os.Pipe()
 	stdinR2, stdinW2, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	pconfig2 := libcontainer.Process{
 	pconfig2 := libcontainer.Process{
 		Env: standardEnvironment,
 		Env: standardEnvironment,
 	}
 	}
@@ -285,19 +243,13 @@ func TestEnter(t *testing.T) {
 	err = container.Start(&pconfig2)
 	err = container.Start(&pconfig2)
 	stdinR2.Close()
 	stdinR2.Close()
 	defer stdinW2.Close()
 	defer stdinW2.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	pid2, err := pconfig2.Pid()
 	pid2, err := pconfig2.Pid()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	processes, err := container.Processes()
 	processes, err := container.Processes()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	n := 0
 	n := 0
 	for i := range processes {
 	for i := range processes {
@@ -318,14 +270,10 @@ func TestEnter(t *testing.T) {
 
 
 	// Check that both processes live in the same pidns
 	// Check that both processes live in the same pidns
 	pidns := string(stdout.Bytes())
 	pidns := string(stdout.Bytes())
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	pidns2 := string(stdout2.Bytes())
 	pidns2 := string(stdout2.Bytes())
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	if pidns != pidns2 {
 	if pidns != pidns2 {
 		t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2)
 		t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2)
@@ -337,28 +285,20 @@ func TestProcessEnv(t *testing.T) {
 		return
 		return
 	}
 	}
 	root, err := newTestRoot()
 	root, err := newTestRoot()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer os.RemoveAll(root)
 	defer os.RemoveAll(root)
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 
 
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	container, err := factory.Create("test", config)
 	container, err := factory.Create("test", config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	var stdout bytes.Buffer
 	var stdout bytes.Buffer
@@ -374,17 +314,12 @@ func TestProcessEnv(t *testing.T) {
 		Stdout: &stdout,
 		Stdout: &stdout,
 	}
 	}
 	err = container.Start(&pconfig)
 	err = container.Start(&pconfig)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	// Wait for process
 	// Wait for process
 	waitProcess(&pconfig, t)
 	waitProcess(&pconfig, t)
 
 
 	outputEnv := string(stdout.Bytes())
 	outputEnv := string(stdout.Bytes())
-	if err != nil {
-		t.Fatal(err)
-	}
 
 
 	// Check that the environment has the key/value pair we added
 	// Check that the environment has the key/value pair we added
 	if !strings.Contains(outputEnv, "FOO=BAR") {
 	if !strings.Contains(outputEnv, "FOO=BAR") {
@@ -402,28 +337,20 @@ func TestProcessCaps(t *testing.T) {
 		return
 		return
 	}
 	}
 	root, err := newTestRoot()
 	root, err := newTestRoot()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer os.RemoveAll(root)
 	defer os.RemoveAll(root)
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 
 
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
 	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	container, err := factory.Create("test", config)
 	container, err := factory.Create("test", config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	processCaps := append(config.Capabilities, "NET_ADMIN")
 	processCaps := append(config.Capabilities, "NET_ADMIN")
@@ -437,17 +364,12 @@ func TestProcessCaps(t *testing.T) {
 		Stdout:       &stdout,
 		Stdout:       &stdout,
 	}
 	}
 	err = container.Start(&pconfig)
 	err = container.Start(&pconfig)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	// Wait for process
 	// Wait for process
 	waitProcess(&pconfig, t)
 	waitProcess(&pconfig, t)
 
 
 	outputStatus := string(stdout.Bytes())
 	outputStatus := string(stdout.Bytes())
-	if err != nil {
-		t.Fatal(err)
-	}
 
 
 	lines := strings.Split(outputStatus, "\n")
 	lines := strings.Split(outputStatus, "\n")
 
 
@@ -497,37 +419,28 @@ func testFreeze(t *testing.T, systemd bool) {
 		return
 		return
 	}
 	}
 	root, err := newTestRoot()
 	root, err := newTestRoot()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer os.RemoveAll(root)
 	defer os.RemoveAll(root)
 
 
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 
 
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
+	cgm := libcontainer.Cgroupfs
 	if systemd {
 	if systemd {
-		config.Cgroups.Slice = "system.slice"
+		cgm = libcontainer.SystemdCgroups
 	}
 	}
 
 
-	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
-	if err != nil {
-		t.Fatal(err)
-	}
+	factory, err := libcontainer.New(root, cgm)
+	ok(t, err)
 
 
 	container, err := factory.Create("test", config)
 	container, err := factory.Create("test", config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	pconfig := libcontainer.Process{
 	pconfig := libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
@@ -537,44 +450,64 @@ func testFreeze(t *testing.T, systemd bool) {
 	err = container.Start(&pconfig)
 	err = container.Start(&pconfig)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	pid, err := pconfig.Pid()
 	pid, err := pconfig.Pid()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	process, err := os.FindProcess(pid)
 	process, err := os.FindProcess(pid)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
-	if err := container.Pause(); err != nil {
-		t.Fatal(err)
-	}
+	err = container.Pause()
+	ok(t, err)
 	state, err := container.Status()
 	state, err := container.Status()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := container.Resume(); err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
+	err = container.Resume()
+	ok(t, err)
 	if state != libcontainer.Paused {
 	if state != libcontainer.Paused {
 		t.Fatal("Unexpected state: ", state)
 		t.Fatal("Unexpected state: ", state)
 	}
 	}
 
 
 	stdinW.Close()
 	stdinW.Close()
 	s, err := process.Wait()
 	s, err := process.Wait()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
+
 	if !s.Success() {
 	if !s.Success() {
 		t.Fatal(s.String())
 		t.Fatal(s.String())
 	}
 	}
 }
 }
 
 
+func TestCpuShares(t *testing.T) {
+	testCpuShares(t, false)
+}
+
+func TestSystemdCpuShares(t *testing.T) {
+	if !systemd.UseSystemd() {
+		t.Skip("Systemd is unsupported")
+	}
+	testCpuShares(t, true)
+}
+
+func testCpuShares(t *testing.T, systemd bool) {
+	if testing.Short() {
+		return
+	}
+	rootfs, err := newRootfs()
+	ok(t, err)
+	defer remove(rootfs)
+
+	config := newTemplateConfig(rootfs)
+	if systemd {
+		config.Cgroups.Slice = "system.slice"
+	}
+	config.Cgroups.CpuShares = 1
+
+	_, _, err = runContainer(config, "", "ps")
+	if err == nil {
+		t.Fatalf("runContainer should failed with invalid CpuShares")
+	}
+}
+
 func TestContainerState(t *testing.T) {
 func TestContainerState(t *testing.T) {
 	if testing.Short() {
 	if testing.Short() {
 		return
 		return
@@ -648,3 +581,185 @@ func TestContainerState(t *testing.T) {
 	stdinW.Close()
 	stdinW.Close()
 	p.Wait()
 	p.Wait()
 }
 }
+
+func TestPassExtraFiles(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+
+	rootfs, err := newRootfs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer remove(rootfs)
+
+	config := newTemplateConfig(rootfs)
+
+	factory, err := libcontainer.New(rootfs, libcontainer.Cgroupfs)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	container, err := factory.Create("test", config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer container.Destroy()
+
+	var stdout bytes.Buffer
+	pipeout1, pipein1, err := os.Pipe()
+	pipeout2, pipein2, err := os.Pipe()
+	process := libcontainer.Process{
+		Args:       []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
+		Env:        []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
+		ExtraFiles: []*os.File{pipein1, pipein2},
+		Stdin:      nil,
+		Stdout:     &stdout,
+	}
+	err = container.Start(&process)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	waitProcess(&process, t)
+
+	out := string(stdout.Bytes())
+	// fd 5 is the directory handle for /proc/$$/fd
+	if out != "0 1 2 3 4 5" {
+		t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to init, got '%s'", out)
+	}
+	var buf = []byte{0}
+	_, err = pipeout1.Read(buf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	out1 := string(buf)
+	if out1 != "1" {
+		t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
+	}
+
+	_, err = pipeout2.Read(buf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	out2 := string(buf)
+	if out2 != "2" {
+		t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
+	}
+}
+
+func TestMountCmds(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+	root, err := newTestRoot()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(root)
+
+	rootfs, err := newRootfs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer remove(rootfs)
+
+	tmpDir, err := ioutil.TempDir("", "tmpdir")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpDir)
+
+	config := newTemplateConfig(rootfs)
+	config.Mounts = append(config.Mounts, &configs.Mount{
+		Source:      tmpDir,
+		Destination: filepath.Join(rootfs, "tmp"),
+		Device:      "bind",
+		Flags:       syscall.MS_BIND | syscall.MS_REC,
+		PremountCmds: []configs.Command{
+			{Path: "touch", Args: []string{filepath.Join(tmpDir, "hello")}},
+			{Path: "touch", Args: []string{filepath.Join(tmpDir, "world")}},
+		},
+		PostmountCmds: []configs.Command{
+			{Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "hello"), filepath.Join(rootfs, "tmp", "hello-backup")}},
+			{Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "world"), filepath.Join(rootfs, "tmp", "world-backup")}},
+		},
+	})
+
+	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	container, err := factory.Create("test", config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer container.Destroy()
+
+	pconfig := libcontainer.Process{
+		Args: []string{"sh", "-c", "env"},
+		Env:  standardEnvironment,
+	}
+	err = container.Start(&pconfig)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Wait for process
+	waitProcess(&pconfig, t)
+
+	entries, err := ioutil.ReadDir(tmpDir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	expected := []string{"hello", "hello-backup", "world", "world-backup"}
+	for i, e := range entries {
+		if e.Name() != expected[i] {
+			t.Errorf("Got(%s), expect %s", e.Name(), expected[i])
+		}
+	}
+}
+
+func TestSystemProperties(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+	root, err := newTestRoot()
+	ok(t, err)
+	defer os.RemoveAll(root)
+
+	rootfs, err := newRootfs()
+	ok(t, err)
+	defer remove(rootfs)
+
+	config := newTemplateConfig(rootfs)
+	config.SystemProperties = map[string]string{
+		"kernel.shmmni": "8192",
+	}
+
+	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
+	ok(t, err)
+
+	container, err := factory.Create("test", config)
+	ok(t, err)
+	defer container.Destroy()
+
+	var stdout bytes.Buffer
+	pconfig := libcontainer.Process{
+		Args:   []string{"sh", "-c", "cat /proc/sys/kernel/shmmni"},
+		Env:    standardEnvironment,
+		Stdin:  nil,
+		Stdout: &stdout,
+	}
+	err = container.Start(&pconfig)
+	ok(t, err)
+
+	// Wait for process
+	waitProcess(&pconfig, t)
+
+	shmmniOutput := strings.TrimSpace(string(stdout.Bytes()))
+	if shmmniOutput != "8192" {
+		t.Fatalf("kernel.shmmni property expected to be 8192, but is %s", shmmniOutput)
+	}
+}

+ 108 - 84
vendor/src/github.com/docker/libcontainer/integration/execin_test.go

@@ -16,22 +16,16 @@ func TestExecIn(t *testing.T) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	container, err := newContainer(config)
 	container, err := newContainer(config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	// Execute a first process in the container
 	// Execute a first process in the container
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	process := &libcontainer.Process{
 	process := &libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
 		Env:   standardEnvironment,
 		Env:   standardEnvironment,
@@ -40,9 +34,7 @@ func TestExecIn(t *testing.T) {
 	err = container.Start(process)
 	err = container.Start(process)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	buffers := newStdBuffers()
 	buffers := newStdBuffers()
 	ps := &libcontainer.Process{
 	ps := &libcontainer.Process{
@@ -53,12 +45,9 @@ func TestExecIn(t *testing.T) {
 		Stderr: buffers.Stderr,
 		Stderr: buffers.Stderr,
 	}
 	}
 	err = container.Start(ps)
 	err = container.Start(ps)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := ps.Wait(); err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
+	_, err = ps.Wait()
+	ok(t, err)
 	stdinW.Close()
 	stdinW.Close()
 	if _, err := process.Wait(); err != nil {
 	if _, err := process.Wait(); err != nil {
 		t.Log(err)
 		t.Log(err)
@@ -74,21 +63,15 @@ func TestExecInRlimit(t *testing.T) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	container, err := newContainer(config)
 	container, err := newContainer(config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	process := &libcontainer.Process{
 	process := &libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
 		Env:   standardEnvironment,
 		Env:   standardEnvironment,
@@ -97,9 +80,7 @@ func TestExecInRlimit(t *testing.T) {
 	err = container.Start(process)
 	err = container.Start(process)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	buffers := newStdBuffers()
 	buffers := newStdBuffers()
 	ps := &libcontainer.Process{
 	ps := &libcontainer.Process{
@@ -110,12 +91,9 @@ func TestExecInRlimit(t *testing.T) {
 		Stderr: buffers.Stderr,
 		Stderr: buffers.Stderr,
 	}
 	}
 	err = container.Start(ps)
 	err = container.Start(ps)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := ps.Wait(); err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
+	_, err = ps.Wait()
+	ok(t, err)
 	stdinW.Close()
 	stdinW.Close()
 	if _, err := process.Wait(); err != nil {
 	if _, err := process.Wait(); err != nil {
 		t.Log(err)
 		t.Log(err)
@@ -131,22 +109,16 @@ func TestExecInError(t *testing.T) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	container, err := newContainer(config)
 	container, err := newContainer(config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	// Execute a first process in the container
 	// Execute a first process in the container
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	process := &libcontainer.Process{
 	process := &libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
 		Env:   standardEnvironment,
 		Env:   standardEnvironment,
@@ -160,9 +132,7 @@ func TestExecInError(t *testing.T) {
 			t.Log(err)
 			t.Log(err)
 		}
 		}
 	}()
 	}()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	unexistent := &libcontainer.Process{
 	unexistent := &libcontainer.Process{
 		Args: []string{"unexistent"},
 		Args: []string{"unexistent"},
@@ -182,22 +152,16 @@ func TestExecInTTY(t *testing.T) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	container, err := newContainer(config)
 	container, err := newContainer(config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	// Execute a first process in the container
 	// Execute a first process in the container
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	process := &libcontainer.Process{
 	process := &libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
 		Env:   standardEnvironment,
 		Env:   standardEnvironment,
@@ -206,9 +170,7 @@ func TestExecInTTY(t *testing.T) {
 	err = container.Start(process)
 	err = container.Start(process)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	var stdout bytes.Buffer
 	var stdout bytes.Buffer
 	ps := &libcontainer.Process{
 	ps := &libcontainer.Process{
@@ -221,21 +183,16 @@ func TestExecInTTY(t *testing.T) {
 		io.Copy(&stdout, console)
 		io.Copy(&stdout, console)
 		close(copy)
 		close(copy)
 	}()
 	}()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	err = container.Start(ps)
 	err = container.Start(ps)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	select {
 	select {
 	case <-time.After(5 * time.Second):
 	case <-time.After(5 * time.Second):
 		t.Fatal("Waiting for copy timed out")
 		t.Fatal("Waiting for copy timed out")
 	case <-copy:
 	case <-copy:
 	}
 	}
-	if _, err := ps.Wait(); err != nil {
-		t.Fatal(err)
-	}
+	_, err = ps.Wait()
+	ok(t, err)
 	stdinW.Close()
 	stdinW.Close()
 	if _, err := process.Wait(); err != nil {
 	if _, err := process.Wait(); err != nil {
 		t.Log(err)
 		t.Log(err)
@@ -251,22 +208,16 @@ func TestExecInEnvironment(t *testing.T) {
 		return
 		return
 	}
 	}
 	rootfs, err := newRootfs()
 	rootfs, err := newRootfs()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer remove(rootfs)
 	defer remove(rootfs)
 	config := newTemplateConfig(rootfs)
 	config := newTemplateConfig(rootfs)
 	container, err := newContainer(config)
 	container, err := newContainer(config)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	defer container.Destroy()
 	defer container.Destroy()
 
 
 	// Execute a first process in the container
 	// Execute a first process in the container
 	stdinR, stdinW, err := os.Pipe()
 	stdinR, stdinW, err := os.Pipe()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	process := &libcontainer.Process{
 	process := &libcontainer.Process{
 		Args:  []string{"cat"},
 		Args:  []string{"cat"},
 		Env:   standardEnvironment,
 		Env:   standardEnvironment,
@@ -275,9 +226,7 @@ func TestExecInEnvironment(t *testing.T) {
 	err = container.Start(process)
 	err = container.Start(process)
 	stdinR.Close()
 	stdinR.Close()
 	defer stdinW.Close()
 	defer stdinW.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 
 
 	buffers := newStdBuffers()
 	buffers := newStdBuffers()
 	process2 := &libcontainer.Process{
 	process2 := &libcontainer.Process{
@@ -293,9 +242,7 @@ func TestExecInEnvironment(t *testing.T) {
 		Stderr: buffers.Stderr,
 		Stderr: buffers.Stderr,
 	}
 	}
 	err = container.Start(process2)
 	err = container.Start(process2)
-	if err != nil {
-		t.Fatal(err)
-	}
+	ok(t, err)
 	if _, err := process2.Wait(); err != nil {
 	if _, err := process2.Wait(); err != nil {
 		out := buffers.Stdout.String()
 		out := buffers.Stdout.String()
 		t.Fatal(err, out)
 		t.Fatal(err, out)
@@ -314,3 +261,80 @@ func TestExecInEnvironment(t *testing.T) {
 		t.Fatalf("unexpected running process, output %q", out)
 		t.Fatalf("unexpected running process, output %q", out)
 	}
 	}
 }
 }
+
+func TestExecinPassExtraFiles(t *testing.T) {
+	if testing.Short() {
+		return
+	}
+	rootfs, err := newRootfs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer remove(rootfs)
+	config := newTemplateConfig(rootfs)
+	container, err := newContainer(config)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer container.Destroy()
+
+	// Execute a first process in the container
+	stdinR, stdinW, err := os.Pipe()
+	if err != nil {
+		t.Fatal(err)
+	}
+	process := &libcontainer.Process{
+		Args:  []string{"cat"},
+		Env:   standardEnvironment,
+		Stdin: stdinR,
+	}
+	err = container.Start(process)
+	stdinR.Close()
+	defer stdinW.Close()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var stdout bytes.Buffer
+	pipeout1, pipein1, err := os.Pipe()
+	pipeout2, pipein2, err := os.Pipe()
+	inprocess := &libcontainer.Process{
+		Args:       []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
+		Env:        []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
+		ExtraFiles: []*os.File{pipein1, pipein2},
+		Stdin:      nil,
+		Stdout:     &stdout,
+	}
+	err = container.Start(inprocess)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	waitProcess(inprocess, t)
+	stdinW.Close()
+	waitProcess(process, t)
+
+	out := string(stdout.Bytes())
+	// fd 5 is the directory handle for /proc/$$/fd
+	if out != "0 1 2 3 4 5" {
+		t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to exec, got '%s'", out)
+	}
+	var buf = []byte{0}
+	_, err = pipeout1.Read(buf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	out1 := string(buf)
+	if out1 != "1" {
+		t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
+	}
+
+	_, err = pipeout2.Read(buf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	out2 := string(buf)
+	if out2 != "2" {
+		t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
+	}
+}

+ 1 - 1
vendor/src/github.com/docker/libcontainer/integration/init_test.go

@@ -21,7 +21,7 @@ func init() {
 	if err != nil {
 	if err != nil {
 		log.Fatalf("unable to initialize for container: %s", err)
 		log.Fatalf("unable to initialize for container: %s", err)
 	}
 	}
-	if err := factory.StartInitialization(3); err != nil {
+	if err := factory.StartInitialization(); err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 }
 }

+ 11 - 0
vendor/src/github.com/docker/libcontainer/integration/utils_test.go

@@ -6,8 +6,11 @@ import (
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
+	"path/filepath"
+	"runtime"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
+	"testing"
 
 
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/configs"
@@ -38,6 +41,14 @@ func (b *stdBuffers) String() string {
 	return strings.Join(s, "|")
 	return strings.Join(s, "|")
 }
 }
 
 
+// ok fails the test if an err is not nil.
+func ok(t testing.TB, err error) {
+	if err != nil {
+		_, file, line, _ := runtime.Caller(1)
+		t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
+	}
+}
+
 // newRootfs creates a new tmp directory and copies the busybox root filesystem
 // newRootfs creates a new tmp directory and copies the busybox root filesystem
 func newRootfs() (string, error) {
 func newRootfs() (string, error) {
 	dir, err := ioutil.TempDir("", "")
 	dir, err := ioutil.TempDir("", "")

+ 13 - 1
vendor/src/github.com/docker/libcontainer/label/label_selinux.go

@@ -101,10 +101,22 @@ func SetFileCreateLabel(fileLabel string) error {
 // the MCS label should continue to be used.  SELinux will use this field
 // the MCS label should continue to be used.  SELinux will use this field
 // to make sure the content can not be shared by other containes.
 // to make sure the content can not be shared by other containes.
 func Relabel(path string, fileLabel string, relabel string) error {
 func Relabel(path string, fileLabel string, relabel string) error {
+	exclude_path := []string{"/", "/usr", "/etc"}
 	if fileLabel == "" {
 	if fileLabel == "" {
 		return nil
 		return nil
 	}
 	}
-	if relabel == "z" {
+	for _, p := range exclude_path {
+		if path == p {
+			return fmt.Errorf("Relabeling of %s is not allowed", path)
+		}
+	}
+	if !strings.ContainsAny(relabel, "zZ") {
+		return nil
+	}
+	if strings.Contains(relabel, "z") && strings.Contains(relabel, "Z") {
+		return fmt.Errorf("Bad SELinux option z and Z can not be used together")
+	}
+	if strings.Contains(relabel, "z") {
 		c := selinux.NewContext(fileLabel)
 		c := selinux.NewContext(fileLabel)
 		c["level"] = "s0"
 		c["level"] = "s0"
 		fileLabel = c.Get()
 		fileLabel = c.Get()

+ 28 - 0
vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go

@@ -87,3 +87,31 @@ func TestDuplicateLabel(t *testing.T) {
 		t.Errorf("DisableSecOpt Failed level incorrect")
 		t.Errorf("DisableSecOpt Failed level incorrect")
 	}
 	}
 }
 }
+func TestRelabel(t *testing.T) {
+	testdir := "/tmp/test"
+	label := "system_u:system_r:svirt_sandbox_file_t:s0:c1,c2"
+	if err := Relabel(testdir, "", "z"); err != nil {
+		t.Fatal("Relabel with no label failed: %v", err)
+	}
+	if err := Relabel(testdir, label, ""); err != nil {
+		t.Fatal("Relabel with no relabel field failed: %v", err)
+	}
+	if err := Relabel(testdir, label, "z"); err != nil {
+		t.Fatal("Relabel shared failed: %v", err)
+	}
+	if err := Relabel(testdir, label, "Z"); err != nil {
+		t.Fatal("Relabel unshared failed: %v", err)
+	}
+	if err := Relabel(testdir, label, "zZ"); err == nil {
+		t.Fatal("Relabel with shared and unshared succeeded")
+	}
+	if err := Relabel("/etc", label, "zZ"); err == nil {
+		t.Fatal("Relabel /etc succeeded")
+	}
+	if err := Relabel("/", label, ""); err == nil {
+		t.Fatal("Relabel / succeeded")
+	}
+	if err := Relabel("/usr", label, "Z"); err == nil {
+		t.Fatal("Relabel /usr succeeded")
+	}
+}

+ 23 - 4
vendor/src/github.com/docker/libcontainer/nsenter/README.md

@@ -1,6 +1,25 @@
 ## nsenter
 ## nsenter
 
 
-The `nsenter` package registers a special init constructor that is called before the Go runtime has 
-a chance to boot.  This provides us the ability to `setns` on existing namespaces and avoid the issues
-that the Go runtime has with multiple threads.  This constructor is only called if this package is 
-registered, imported, in your go application and the argv 0 is `nsenter`.
+The `nsenter` package registers a special init constructor that is called before 
+the Go runtime has a chance to boot.  This provides us the ability to `setns` on 
+existing namespaces and avoid the issues that the Go runtime has with multiple 
+threads.  This constructor will be called if this package is registered, 
+imported, in your go application.
+
+The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd/cgo/)
+package. In cgo, if the import of "C" is immediately preceded by a comment, that comment, 
+called the preamble, is used as a header when compiling the C parts of the package.
+So every time we  import package `nsenter`, the C code function `nsexec()` would be 
+called. And package `nsenter` is now only imported in Docker execdriver, so every time 
+before we call `execdriver.Exec()`, that C code would run.
+
+`nsexec()` will first check the environment variable `_LIBCONTAINER_INITPID` 
+which will give the process of the container that should be joined. Namespaces fd will 
+be found from `/proc/[pid]/ns` and set by `setns` syscall.
+
+And then get the pipe number from `_LIBCONTAINER_INITPIPE`, error message could
+be transfered through it. If tty is added, `_LIBCONTAINER_CONSOLE_PATH` will 
+have value and start a console for output.
+
+Finally, `nsexec()` will clone a child process , exit the parent process and let 
+the Go runtime take over.

+ 1 - 1
vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go

@@ -24,7 +24,7 @@ func TestNsenterAlivePid(t *testing.T) {
 		Path:       os.Args[0],
 		Path:       os.Args[0],
 		Args:       args,
 		Args:       args,
 		ExtraFiles: []*os.File{w},
 		ExtraFiles: []*os.File{w},
-		Env:        []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", os.Getpid())},
+		Env:        []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", os.Getpid()), "_LIBCONTAINER_INITPIPE=3"},
 	}
 	}
 
 
 	if err := cmd.Start(); err != nil {
 	if err := cmd.Start(); err != nil {

+ 21 - 2
vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c

@@ -66,7 +66,7 @@ void nsexec()
 	const int num = sizeof(namespaces) / sizeof(char *);
 	const int num = sizeof(namespaces) / sizeof(char *);
 	jmp_buf env;
 	jmp_buf env;
 	char buf[PATH_MAX], *val;
 	char buf[PATH_MAX], *val;
-	int i, tfd, child, len, consolefd = -1;
+	int i, tfd, child, len, pipenum, consolefd = -1;
 	pid_t pid;
 	pid_t pid;
 	char *console;
 	char *console;
 
 
@@ -81,6 +81,19 @@ void nsexec()
 		exit(1);
 		exit(1);
 	}
 	}
 
 
+	val = getenv("_LIBCONTAINER_INITPIPE");
+	if (val == NULL) {
+		pr_perror("Child pipe not found");
+		exit(1);
+	}
+
+	pipenum = atoi(val);
+	snprintf(buf, sizeof(buf), "%d", pipenum);
+	if (strcmp(val, buf)) {
+		pr_perror("Unable to parse _LIBCONTAINER_INITPIPE");
+		exit(1);
+	}
+
 	console = getenv("_LIBCONTAINER_CONSOLE_PATH");
 	console = getenv("_LIBCONTAINER_CONSOLE_PATH");
 	if (console != NULL) {
 	if (console != NULL) {
 		consolefd = open(console, O_RDWR);
 		consolefd = open(console, O_RDWR);
@@ -124,6 +137,8 @@ void nsexec()
 	}
 	}
 
 
 	if (setjmp(env) == 1) {
 	if (setjmp(env) == 1) {
+		// Child
+
 		if (setsid() == -1) {
 		if (setsid() == -1) {
 			pr_perror("setsid failed");
 			pr_perror("setsid failed");
 			exit(1);
 			exit(1);
@@ -149,7 +164,11 @@ void nsexec()
 		// Finish executing, let the Go runtime take over.
 		// Finish executing, let the Go runtime take over.
 		return;
 		return;
 	}
 	}
+	// Parent
 
 
+	// We must fork to actually enter the PID namespace, use CLONE_PARENT
+	// so the child can have the right parent, and we don't need to forward
+	// the child's exit code or resend its death signal.
 	child = clone_parent(&env);
 	child = clone_parent(&env);
 	if (child < 0) {
 	if (child < 0) {
 		pr_perror("Unable to fork");
 		pr_perror("Unable to fork");
@@ -158,7 +177,7 @@ void nsexec()
 
 
 	len = snprintf(buf, sizeof(buf), "{ \"pid\" : %d }\n", child);
 	len = snprintf(buf, sizeof(buf), "{ \"pid\" : %d }\n", child);
 
 
-	if (write(3, buf, len) != len) {
+	if (write(pipenum, buf, len) != len) {
 		pr_perror("Unable to send a child pid");
 		pr_perror("Unable to send a child pid");
 		kill(child, SIGKILL);
 		kill(child, SIGKILL);
 		exit(1);
 		exit(1);

+ 45 - 0
vendor/src/github.com/docker/libcontainer/nsinit/README.md

@@ -65,3 +65,48 @@ You can identify if a process is running in a container by looking to see if
    
    
 You may also specify an alternate root directory from where the `container.json`
 You may also specify an alternate root directory from where the `container.json`
 file is read and where the `state.json` file will be saved.
 file is read and where the `state.json` file will be saved.
+
+### How to use?
+
+Currently nsinit has 9 commands. Type `nsinit -h` to list all of them. 
+And for every alternative command, you can also use `--help` to get more 
+detailed help documents. For example, `nsinit config --help`.
+
+`nsinit` cli application is implemented using [cli.go](https://github.com/codegangsta/cli). 
+Lots of details are handled in cli.go, so the implementation of `nsinit` itself 
+is very clean and clear.
+
+*   **config**	
+It will generate a standard configuration file for a container.  By default, it 
+will generate as the template file in [config.go](https://github.com/docker/libcontainer/blob/master/nsinit/config.go#L192). 
+It will modify the template if you have specified some configuration by options.
+*   **exec**	
+Starts a container and execute a new command inside it. Besides common options, it
+has some special options as below.
+	- `--tty,-t`: allocate a TTY to the container.
+	- `--config`: you can specify a configuration file. By default, it will use 
+	template configuration.
+	- `--id`: specify the ID for a container. By default, the id is "nsinit".
+	- `--user,-u`: set the user, uid, and/or gid for the process. By default the 
+	value is "root".
+	- `--cwd`: set the current working dir.
+	- `--env`: set environment variables for the process.
+*   **init**		
+It's an internal command that is called inside the container's namespaces to 
+initialize the namespace and exec the user's process. It should not be called 
+externally.
+*   **oom**		
+Display oom notifications for a container, you should specify container id.
+*   **pause**	
+Pause the container's processes, you should specify container id. It will use 
+cgroup freeze subsystem to help.
+*   **unpause**		
+Unpause the container's processes. Same with `pause`.
+*   **stats**	
+Display statistics for the container, it will mainly show cgroup and network 
+statistics.
+*   **state**	
+Get the container's current state. You can also read the state from `state.json`
+ in your container_id folder.
+*   **help, h**		
+Shows a list of commands or help for one command.

+ 7 - 0
vendor/src/github.com/docker/libcontainer/nsinit/config.go

@@ -43,6 +43,7 @@ var createFlags = []cli.Flag{
 	cli.StringFlag{Name: "veth-address", Usage: "veth ip address"},
 	cli.StringFlag{Name: "veth-address", Usage: "veth ip address"},
 	cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"},
 	cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"},
 	cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"},
 	cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"},
+	cli.BoolFlag{Name: "cgroup", Usage: "mount the cgroup data for the container"},
 }
 }
 
 
 var configCommand = cli.Command{
 var configCommand = cli.Command{
@@ -187,6 +188,12 @@ func modify(config *configs.Config, context *cli.Context) {
 		}
 		}
 		config.Networks = append(config.Networks, network)
 		config.Networks = append(config.Networks, network)
 	}
 	}
+	if context.Bool("cgroup") {
+		config.Mounts = append(config.Mounts, &configs.Mount{
+			Destination: "/sys/fs/cgroup",
+			Device:      "cgroup",
+		})
+	}
 }
 }
 
 
 func getTemplate() *configs.Config {
 func getTemplate() *configs.Config {

+ 1 - 0
vendor/src/github.com/docker/libcontainer/nsinit/exec.go

@@ -23,6 +23,7 @@ var execCommand = cli.Command{
 	Action: execAction,
 	Action: execAction,
 	Flags: append([]cli.Flag{
 	Flags: append([]cli.Flag{
 		cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
 		cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
+		cli.BoolFlag{Name: "systemd", Usage: "Use systemd for managing cgroups, if available"},
 		cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
 		cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
 		cli.StringFlag{Name: "config", Value: "", Usage: "path to the configuration file"},
 		cli.StringFlag{Name: "config", Value: "", Usage: "path to the configuration file"},
 		cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"},
 		cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"},

+ 1 - 1
vendor/src/github.com/docker/libcontainer/nsinit/init.go

@@ -20,7 +20,7 @@ var initCommand = cli.Command{
 		if err != nil {
 		if err != nil {
 			fatal(err)
 			fatal(err)
 		}
 		}
-		if err := factory.StartInitialization(3); err != nil {
+		if err := factory.StartInitialization(); err != nil {
 			fatal(err)
 			fatal(err)
 		}
 		}
 		panic("This line should never been executed")
 		panic("This line should never been executed")

+ 1 - 2
vendor/src/github.com/docker/libcontainer/nsinit/oom.go

@@ -1,8 +1,7 @@
 package main
 package main
 
 
 import (
 import (
-	"log"
-
+	log "github.com/Sirupsen/logrus"
 	"github.com/codegangsta/cli"
 	"github.com/codegangsta/cli"
 )
 )
 
 

+ 1 - 2
vendor/src/github.com/docker/libcontainer/nsinit/pause.go

@@ -1,8 +1,7 @@
 package main
 package main
 
 
 import (
 import (
-	"log"
-
+	log "github.com/Sirupsen/logrus"
 	"github.com/codegangsta/cli"
 	"github.com/codegangsta/cli"
 )
 )
 
 

+ 11 - 1
vendor/src/github.com/docker/libcontainer/nsinit/utils.go

@@ -3,10 +3,12 @@ package main
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
+	log "github.com/Sirupsen/logrus"
 	"os"
 	"os"
 
 
 	"github.com/codegangsta/cli"
 	"github.com/codegangsta/cli"
 	"github.com/docker/libcontainer"
 	"github.com/docker/libcontainer"
+	"github.com/docker/libcontainer/cgroups/systemd"
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/configs"
 )
 )
 
 
@@ -29,7 +31,15 @@ func loadConfig(context *cli.Context) (*configs.Config, error) {
 }
 }
 
 
 func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
 func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
-	return libcontainer.New(context.GlobalString("root"), libcontainer.Cgroupfs)
+	cgm := libcontainer.Cgroupfs
+	if context.Bool("systemd") {
+		if systemd.UseSystemd() {
+			cgm = libcontainer.SystemdCgroups
+		} else {
+			log.Warn("systemd cgroup flag passed, but systemd support for managing cgroups is not available.")
+		}
+	}
+	return libcontainer.New(context.GlobalString("root"), cgm)
 }
 }
 
 
 func getContainer(context *cli.Context) (libcontainer.Container, error) {
 func getContainer(context *cli.Context) (libcontainer.Container, error) {

+ 5 - 2
vendor/src/github.com/docker/libcontainer/process.go

@@ -23,7 +23,7 @@ type Process struct {
 	Env []string
 	Env []string
 
 
 	// User will set the uid and gid of the executing process running inside the container
 	// User will set the uid and gid of the executing process running inside the container
-	// local to the contaienr's user and group configuration.
+	// local to the container's user and group configuration.
 	User string
 	User string
 
 
 	// Cwd will change the processes current working directory inside the container's rootfs.
 	// Cwd will change the processes current working directory inside the container's rootfs.
@@ -38,11 +38,14 @@ type Process struct {
 	// Stderr is a pointer to a writer which receives the standard error stream.
 	// Stderr is a pointer to a writer which receives the standard error stream.
 	Stderr io.Writer
 	Stderr io.Writer
 
 
+	// ExtraFiles specifies additional open files to be inherited by the container
+	ExtraFiles []*os.File
+
 	// consolePath is the path to the console allocated to the container.
 	// consolePath is the path to the console allocated to the container.
 	consolePath string
 	consolePath string
 
 
 	// 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 capabilities not specified will be dropped from the processes capability mask
 	Capabilities []string
 	Capabilities []string
 
 
 	ops processOperations
 	ops processOperations

+ 3 - 0
vendor/src/github.com/docker/libcontainer/process_linux.go

@@ -119,6 +119,9 @@ func (p *setnsProcess) execSetns() error {
 // terminate sends a SIGKILL to the forked process for the setns routine then waits to
 // terminate sends a SIGKILL to the forked process for the setns routine then waits to
 // avoid the process becomming a zombie.
 // avoid the process becomming a zombie.
 func (p *setnsProcess) terminate() error {
 func (p *setnsProcess) terminate() error {
+	if p.cmd.Process == nil {
+		return nil
+	}
 	err := p.cmd.Process.Kill()
 	err := p.cmd.Process.Kill()
 	if _, werr := p.wait(); err == nil {
 	if _, werr := p.wait(); err == nil {
 		err = werr
 		err = werr

+ 76 - 4
vendor/src/github.com/docker/libcontainer/rootfs_linux.go

@@ -6,11 +6,14 @@ import (
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
+	"os/exec"
+	"path"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
 
 
+	"github.com/docker/libcontainer/cgroups"
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/configs"
 	"github.com/docker/libcontainer/label"
 	"github.com/docker/libcontainer/label"
 )
 )
@@ -24,9 +27,20 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
 		return newSystemError(err)
 		return newSystemError(err)
 	}
 	}
 	for _, m := range config.Mounts {
 	for _, m := range config.Mounts {
+		for _, precmd := range m.PremountCmds {
+			if err := mountCmd(precmd); err != nil {
+				return newSystemError(err)
+			}
+		}
 		if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil {
 		if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil {
 			return newSystemError(err)
 			return newSystemError(err)
 		}
 		}
+
+		for _, postcmd := range m.PostmountCmds {
+			if err := mountCmd(postcmd); err != nil {
+				return newSystemError(err)
+			}
+		}
 	}
 	}
 	if err := createDevices(config); err != nil {
 	if err := createDevices(config); err != nil {
 		return newSystemError(err)
 		return newSystemError(err)
@@ -62,6 +76,18 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) {
 	return nil
 	return nil
 }
 }
 
 
+func mountCmd(cmd configs.Command) error {
+
+	command := exec.Command(cmd.Path, cmd.Args[:]...)
+	command.Env = cmd.Env
+	command.Dir = cmd.Dir
+	if out, err := command.CombinedOutput(); err != nil {
+		return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err)
+	}
+
+	return nil
+}
+
 func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
 func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
 	var (
 	var (
 		dest = m.Destination
 		dest = m.Destination
@@ -72,11 +98,19 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
 	}
 	}
 
 
 	switch m.Device {
 	switch m.Device {
-	case "proc", "mqueue", "sysfs":
+	case "proc", "sysfs":
 		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
 		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
 			return err
 			return err
 		}
 		}
 		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "")
 		return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "")
+	case "mqueue":
+		if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) {
+			return err
+		}
+		if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), ""); err != nil {
+			return err
+		}
+		return label.SetFileLabel(dest, mountLabel)
 	case "tmpfs":
 	case "tmpfs":
 		stat, err := os.Stat(dest)
 		stat, err := os.Stat(dest)
 		if err != nil {
 		if err != nil {
@@ -126,6 +160,37 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
 				return err
 				return err
 			}
 			}
 		}
 		}
+	case "cgroup":
+		mounts, err := cgroups.GetCgroupMounts()
+		if err != nil {
+			return err
+		}
+		var binds []*configs.Mount
+		for _, mm := range mounts {
+			dir, err := mm.GetThisCgroupDir()
+			if err != nil {
+				return err
+			}
+			binds = append(binds, &configs.Mount{
+				Device:      "bind",
+				Source:      filepath.Join(mm.Mountpoint, dir),
+				Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")),
+				Flags:       syscall.MS_BIND | syscall.MS_REC | syscall.MS_RDONLY,
+			})
+		}
+		tmpfs := &configs.Mount{
+			Device:      "tmpfs",
+			Destination: m.Destination,
+			Flags:       syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV,
+		}
+		if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil {
+			return err
+		}
+		for _, b := range binds {
+			if err := mountToRootfs(b, rootfs, mountLabel); err != nil {
+				return err
+			}
+		}
 	default:
 	default:
 		return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination)
 		return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination)
 	}
 	}
@@ -240,9 +305,9 @@ func mknodDevice(dest string, node *configs.Device) error {
 }
 }
 
 
 func prepareRoot(config *configs.Config) error {
 func prepareRoot(config *configs.Config) error {
-	flag := syscall.MS_PRIVATE | syscall.MS_REC
-	if config.NoPivotRoot {
-		flag = syscall.MS_SLAVE | syscall.MS_REC
+	flag := syscall.MS_SLAVE | syscall.MS_REC
+	if config.Privatefs {
+		flag = syscall.MS_PRIVATE | syscall.MS_REC
 	}
 	}
 	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
 	if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
 		return err
 		return err
@@ -355,3 +420,10 @@ func maskFile(path string) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+// writeSystemProperty writes the value to a path under /proc/sys as determined from the key.
+// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward.
+func writeSystemProperty(key, value string) error {
+	keyPath := strings.Replace(key, ".", "/", -1)
+	return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644)
+}

+ 7 - 0
vendor/src/github.com/docker/libcontainer/standard_init_linux.go

@@ -64,6 +64,13 @@ func (l *linuxStandardInit) Init() error {
 	if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil {
 	if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil {
 		return err
 		return err
 	}
 	}
+
+	for key, value := range l.config.Config.SystemProperties {
+		if err := writeSystemProperty(key, value); err != nil {
+			return err
+		}
+	}
+
 	for _, path := range l.config.Config.ReadonlyPaths {
 	for _, path := range l.config.Config.ReadonlyPaths {
 		if err := remountReadonly(path); err != nil {
 		if err := remountReadonly(path); err != nil {
 			return err
 			return err

+ 3 - 1
vendor/src/github.com/docker/libcontainer/system/setns_linux.go

@@ -12,8 +12,10 @@ import (
 // We are declaring the macro here because the SETNS syscall does not exist in th stdlib
 // We are declaring the macro here because the SETNS syscall does not exist in th stdlib
 var setNsMap = map[string]uintptr{
 var setNsMap = map[string]uintptr{
 	"linux/386":     346,
 	"linux/386":     346,
+	"linux/arm64":   268,
 	"linux/amd64":   308,
 	"linux/amd64":   308,
-	"linux/arm":     374,
+	"linux/arm":     375,
+	"linux/ppc":     350,
 	"linux/ppc64":   350,
 	"linux/ppc64":   350,
 	"linux/ppc64le": 350,
 	"linux/ppc64le": 350,
 	"linux/s390x":   339,
 	"linux/s390x":   339,

+ 1 - 1
vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go

@@ -1,4 +1,4 @@
-// +build linux,amd64 linux,ppc64 linux,ppc64le linux,s390x
+// +build linux,arm64 linux,amd64 linux,ppc linux,ppc64 linux,ppc64le linux,s390x
 
 
 package system
 package system
 
 

+ 1 - 1
vendor/src/github.com/docker/libcontainer/update-vendor.sh

@@ -43,7 +43,7 @@ clone() {
 clone git github.com/codegangsta/cli 1.1.0
 clone git github.com/codegangsta/cli 1.1.0
 clone git github.com/coreos/go-systemd v2
 clone git github.com/coreos/go-systemd v2
 clone git github.com/godbus/dbus v2
 clone git github.com/godbus/dbus v2
-clone git github.com/Sirupsen/logrus v0.6.6
+clone git github.com/Sirupsen/logrus v0.7.3
 clone git github.com/syndtr/gocapability 8e4cdcb
 clone git github.com/syndtr/gocapability 8e4cdcb
 
 
 # intentionally not vendoring Docker itself...  that'd be a circle :)
 # intentionally not vendoring Docker itself...  that'd be a circle :)