| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,15 +0,0 @@ |
| 1 |
-// +build !linux |
|
| 2 |
- |
|
| 3 |
-package cgroups |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func useSystemd() bool {
|
|
| 10 |
- return false |
|
| 11 |
-} |
|
| 12 |
- |
|
| 13 |
-func systemdApply(c *Cgroup, pid int) (ActiveCgroup, error) {
|
|
| 14 |
- return nil, fmt.Errorf("Systemd not supported")
|
|
| 15 |
-} |
| 16 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,164 +0,0 @@ |
| 1 |
-// +build linux |
|
| 2 |
- |
|
| 3 |
-package cgroups |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "fmt" |
|
| 7 |
- systemd1 "github.com/coreos/go-systemd/dbus" |
|
| 8 |
- "github.com/dotcloud/docker/pkg/systemd" |
|
| 9 |
- "github.com/godbus/dbus" |
|
| 10 |
- "path/filepath" |
|
| 11 |
- "strings" |
|
| 12 |
- "sync" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type systemdCgroup struct {
|
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-var ( |
|
| 19 |
- connLock sync.Mutex |
|
| 20 |
- theConn *systemd1.Conn |
|
| 21 |
- hasStartTransientUnit bool |
|
| 22 |
-) |
|
| 23 |
- |
|
| 24 |
-func useSystemd() bool {
|
|
| 25 |
- if !systemd.SdBooted() {
|
|
| 26 |
- return false |
|
| 27 |
- } |
|
| 28 |
- |
|
| 29 |
- connLock.Lock() |
|
| 30 |
- defer connLock.Unlock() |
|
| 31 |
- |
|
| 32 |
- if theConn == nil {
|
|
| 33 |
- var err error |
|
| 34 |
- theConn, err = systemd1.New() |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return false |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- // Assume we have StartTransientUnit |
|
| 40 |
- hasStartTransientUnit = true |
|
| 41 |
- |
|
| 42 |
- // But if we get UnknownMethod error we don't |
|
| 43 |
- if _, err := theConn.StartTransientUnit("test.scope", "invalid"); err != nil {
|
|
| 44 |
- if dbusError, ok := err.(dbus.Error); ok {
|
|
| 45 |
- if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" {
|
|
| 46 |
- hasStartTransientUnit = false |
|
| 47 |
- } |
|
| 48 |
- } |
|
| 49 |
- } |
|
| 50 |
- } |
|
| 51 |
- |
|
| 52 |
- return hasStartTransientUnit |
|
| 53 |
-} |
|
| 54 |
- |
|
| 55 |
-type DeviceAllow struct {
|
|
| 56 |
- Node string |
|
| 57 |
- Permissions string |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 |
-func getIfaceForUnit(unitName string) string {
|
|
| 61 |
- if strings.HasSuffix(unitName, ".scope") {
|
|
| 62 |
- return "Scope" |
|
| 63 |
- } |
|
| 64 |
- if strings.HasSuffix(unitName, ".service") {
|
|
| 65 |
- return "Service" |
|
| 66 |
- } |
|
| 67 |
- return "Unit" |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-func systemdApply(c *Cgroup, pid int) (ActiveCgroup, error) {
|
|
| 71 |
- unitName := c.Parent + "-" + c.Name + ".scope" |
|
| 72 |
- slice := "system.slice" |
|
| 73 |
- |
|
| 74 |
- var properties []systemd1.Property |
|
| 75 |
- |
|
| 76 |
- for _, v := range c.UnitProperties {
|
|
| 77 |
- switch v[0] {
|
|
| 78 |
- case "Slice": |
|
| 79 |
- slice = v[1] |
|
| 80 |
- default: |
|
| 81 |
- return nil, fmt.Errorf("Unknown unit propery %s", v[0])
|
|
| 82 |
- } |
|
| 83 |
- } |
|
| 84 |
- |
|
| 85 |
- properties = append(properties, |
|
| 86 |
- systemd1.Property{"Slice", dbus.MakeVariant(slice)},
|
|
| 87 |
- systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)},
|
|
| 88 |
- systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})})
|
|
| 89 |
- |
|
| 90 |
- if !c.DeviceAccess {
|
|
| 91 |
- properties = append(properties, |
|
| 92 |
- systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")},
|
|
| 93 |
- systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{
|
|
| 94 |
- {"/dev/null", "rwm"},
|
|
| 95 |
- {"/dev/zero", "rwm"},
|
|
| 96 |
- {"/dev/full", "rwm"},
|
|
| 97 |
- {"/dev/random", "rwm"},
|
|
| 98 |
- {"/dev/urandom", "rwm"},
|
|
| 99 |
- {"/dev/tty", "rwm"},
|
|
| 100 |
- {"/dev/console", "rwm"},
|
|
| 101 |
- {"/dev/tty0", "rwm"},
|
|
| 102 |
- {"/dev/tty1", "rwm"},
|
|
| 103 |
- {"/dev/pts/ptmx", "rwm"},
|
|
| 104 |
- // There is no way to add /dev/pts/* here atm, so we hack this manually below |
|
| 105 |
- // /dev/pts/* (how to add this?) |
|
| 106 |
- // Same with tuntap, which doesn't exist as a node most of the time |
|
| 107 |
- })}) |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- // Always enable accounting, this gets us the same behaviour as the raw implementation, |
|
| 111 |
- // plus the kernel has some problems with joining the memory cgroup at a later time. |
|
| 112 |
- properties = append(properties, |
|
| 113 |
- systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)},
|
|
| 114 |
- systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)})
|
|
| 115 |
- |
|
| 116 |
- if c.Memory != 0 {
|
|
| 117 |
- properties = append(properties, |
|
| 118 |
- systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
|
| 119 |
- } |
|
| 120 |
- // TODO: MemorySwap not available in systemd |
|
| 121 |
- |
|
| 122 |
- if c.CpuShares != 0 {
|
|
| 123 |
- properties = append(properties, |
|
| 124 |
- systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))})
|
|
| 125 |
- } |
|
| 126 |
- |
|
| 127 |
- if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil {
|
|
| 128 |
- return nil, err |
|
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- // To work around the lack of /dev/pts/* support above we need to manually add these |
|
| 132 |
- // so, ask systemd for the cgroup used |
|
| 133 |
- props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) |
|
| 134 |
- if err != nil {
|
|
| 135 |
- return nil, err |
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 |
- cgroup := props["ControlGroup"].(string) |
|
| 139 |
- |
|
| 140 |
- if !c.DeviceAccess {
|
|
| 141 |
- mountpoint, err := FindCgroupMountpoint("devices")
|
|
| 142 |
- if err != nil {
|
|
| 143 |
- return nil, err |
|
| 144 |
- } |
|
| 145 |
- |
|
| 146 |
- path := filepath.Join(mountpoint, cgroup) |
|
| 147 |
- |
|
| 148 |
- // /dev/pts/* |
|
| 149 |
- if err := writeFile(path, "devices.allow", "c 136:* rwm"); err != nil {
|
|
| 150 |
- return nil, err |
|
| 151 |
- } |
|
| 152 |
- // tuntap |
|
| 153 |
- if err := writeFile(path, "devices.allow", "c 10:200 rwm"); err != nil {
|
|
| 154 |
- return nil, err |
|
| 155 |
- } |
|
| 156 |
- } |
|
| 157 |
- |
|
| 158 |
- return &systemdCgroup{}, nil
|
|
| 159 |
-} |
|
| 160 |
- |
|
| 161 |
-func (c *systemdCgroup) Cleanup() error {
|
|
| 162 |
- // systemd cleans up, we don't need to do anything |
|
| 163 |
- return nil |
|
| 164 |
-} |
| ... | ... |
@@ -24,16 +24,3 @@ type Cgroup struct {
|
| 24 | 24 |
type ActiveCgroup interface {
|
| 25 | 25 |
Cleanup() error |
| 26 | 26 |
} |
| 27 |
- |
|
| 28 |
-func Apply(c *Cgroup, pid int) (ActiveCgroup, error) {
|
|
| 29 |
- // We have two implementation of cgroups support, one is based on |
|
| 30 |
- // systemd and the dbus api, and one is based on raw cgroup fs operations |
|
| 31 |
- // following the pre-single-writer model docs at: |
|
| 32 |
- // http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/ |
|
| 33 |
- |
|
| 34 |
- if useSystemd() {
|
|
| 35 |
- return systemdApply(c, pid) |
|
| 36 |
- } else {
|
|
| 37 |
- return rawApply(c, pid) |
|
| 38 |
- } |
|
| 39 |
-} |
| 40 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+// +build !linux |
|
| 1 |
+ |
|
| 2 |
+package systemd |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "github.com/dotcloud/docker/pkg/cgroups" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func UseSystemd() bool {
|
|
| 10 |
+ return false |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+func systemdApply(c *Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
|
| 14 |
+ return nil, fmt.Errorf("Systemd not supported")
|
|
| 15 |
+} |
| 0 | 16 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,167 @@ |
| 0 |
+// +build linux |
|
| 1 |
+ |
|
| 2 |
+package systemd |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io/ioutil" |
|
| 7 |
+ "path/filepath" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "sync" |
|
| 10 |
+ |
|
| 11 |
+ systemd1 "github.com/coreos/go-systemd/dbus" |
|
| 12 |
+ "github.com/dotcloud/docker/pkg/cgroups" |
|
| 13 |
+ "github.com/dotcloud/docker/pkg/systemd" |
|
| 14 |
+ "github.com/godbus/dbus" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+type systemdCgroup struct {
|
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+type DeviceAllow struct {
|
|
| 21 |
+ Node string |
|
| 22 |
+ Permissions string |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+var ( |
|
| 26 |
+ connLock sync.Mutex |
|
| 27 |
+ theConn *systemd1.Conn |
|
| 28 |
+ hasStartTransientUnit bool |
|
| 29 |
+) |
|
| 30 |
+ |
|
| 31 |
+func UseSystemd() bool {
|
|
| 32 |
+ if !systemd.SdBooted() {
|
|
| 33 |
+ return false |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ connLock.Lock() |
|
| 37 |
+ defer connLock.Unlock() |
|
| 38 |
+ |
|
| 39 |
+ if theConn == nil {
|
|
| 40 |
+ var err error |
|
| 41 |
+ theConn, err = systemd1.New() |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ return false |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ // Assume we have StartTransientUnit |
|
| 47 |
+ hasStartTransientUnit = true |
|
| 48 |
+ |
|
| 49 |
+ // But if we get UnknownMethod error we don't |
|
| 50 |
+ if _, err := theConn.StartTransientUnit("test.scope", "invalid"); err != nil {
|
|
| 51 |
+ if dbusError, ok := err.(dbus.Error); ok {
|
|
| 52 |
+ if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" {
|
|
| 53 |
+ hasStartTransientUnit = false |
|
| 54 |
+ } |
|
| 55 |
+ } |
|
| 56 |
+ } |
|
| 57 |
+ } |
|
| 58 |
+ return hasStartTransientUnit |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func getIfaceForUnit(unitName string) string {
|
|
| 62 |
+ if strings.HasSuffix(unitName, ".scope") {
|
|
| 63 |
+ return "Scope" |
|
| 64 |
+ } |
|
| 65 |
+ if strings.HasSuffix(unitName, ".service") {
|
|
| 66 |
+ return "Service" |
|
| 67 |
+ } |
|
| 68 |
+ return "Unit" |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
|
| 72 |
+ var ( |
|
| 73 |
+ unitName = c.Parent + "-" + c.Name + ".scope" |
|
| 74 |
+ slice = "system.slice" |
|
| 75 |
+ properties []systemd1.Property |
|
| 76 |
+ ) |
|
| 77 |
+ |
|
| 78 |
+ for _, v := range c.UnitProperties {
|
|
| 79 |
+ switch v[0] {
|
|
| 80 |
+ case "Slice": |
|
| 81 |
+ slice = v[1] |
|
| 82 |
+ default: |
|
| 83 |
+ return nil, fmt.Errorf("Unknown unit propery %s", v[0])
|
|
| 84 |
+ } |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ properties = append(properties, |
|
| 88 |
+ systemd1.Property{"Slice", dbus.MakeVariant(slice)},
|
|
| 89 |
+ systemd1.Property{"Description", dbus.MakeVariant("docker container " + c.Name)},
|
|
| 90 |
+ systemd1.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})},
|
|
| 91 |
+ ) |
|
| 92 |
+ |
|
| 93 |
+ if !c.DeviceAccess {
|
|
| 94 |
+ properties = append(properties, |
|
| 95 |
+ systemd1.Property{"DevicePolicy", dbus.MakeVariant("strict")},
|
|
| 96 |
+ systemd1.Property{"DeviceAllow", dbus.MakeVariant([]DeviceAllow{
|
|
| 97 |
+ {"/dev/null", "rwm"},
|
|
| 98 |
+ {"/dev/zero", "rwm"},
|
|
| 99 |
+ {"/dev/full", "rwm"},
|
|
| 100 |
+ {"/dev/random", "rwm"},
|
|
| 101 |
+ {"/dev/urandom", "rwm"},
|
|
| 102 |
+ {"/dev/tty", "rwm"},
|
|
| 103 |
+ {"/dev/console", "rwm"},
|
|
| 104 |
+ {"/dev/tty0", "rwm"},
|
|
| 105 |
+ {"/dev/tty1", "rwm"},
|
|
| 106 |
+ {"/dev/pts/ptmx", "rwm"},
|
|
| 107 |
+ // There is no way to add /dev/pts/* here atm, so we hack this manually below |
|
| 108 |
+ // /dev/pts/* (how to add this?) |
|
| 109 |
+ // Same with tuntap, which doesn't exist as a node most of the time |
|
| 110 |
+ })}) |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ // Always enable accounting, this gets us the same behaviour as the raw implementation, |
|
| 114 |
+ // plus the kernel has some problems with joining the memory cgroup at a later time. |
|
| 115 |
+ properties = append(properties, |
|
| 116 |
+ systemd1.Property{"MemoryAccounting", dbus.MakeVariant(true)},
|
|
| 117 |
+ systemd1.Property{"CPUAccounting", dbus.MakeVariant(true)})
|
|
| 118 |
+ |
|
| 119 |
+ if c.Memory != 0 {
|
|
| 120 |
+ properties = append(properties, |
|
| 121 |
+ systemd1.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
|
| 122 |
+ } |
|
| 123 |
+ // TODO: MemorySwap not available in systemd |
|
| 124 |
+ |
|
| 125 |
+ if c.CpuShares != 0 {
|
|
| 126 |
+ properties = append(properties, |
|
| 127 |
+ systemd1.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))})
|
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil {
|
|
| 131 |
+ return nil, err |
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ // To work around the lack of /dev/pts/* support above we need to manually add these |
|
| 135 |
+ // so, ask systemd for the cgroup used |
|
| 136 |
+ props, err := theConn.GetUnitTypeProperties(unitName, getIfaceForUnit(unitName)) |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ return nil, err |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ cgroup := props["ControlGroup"].(string) |
|
| 142 |
+ |
|
| 143 |
+ if !c.DeviceAccess {
|
|
| 144 |
+ mountpoint, err := cgroups.FindCgroupMountpoint("devices")
|
|
| 145 |
+ if err != nil {
|
|
| 146 |
+ return nil, err |
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+ path := filepath.Join(mountpoint, cgroup) |
|
| 150 |
+ |
|
| 151 |
+ // /dev/pts/* |
|
| 152 |
+ if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 136:* rwm"), 0700); err != nil {
|
|
| 153 |
+ return nil, err |
|
| 154 |
+ } |
|
| 155 |
+ // tuntap |
|
| 156 |
+ if err := ioutil.WriteFile(filepath.Join(path, "devices.allow"), []byte("c 10:200 rwm"), 0700); err != nil {
|
|
| 157 |
+ return nil, err |
|
| 158 |
+ } |
|
| 159 |
+ } |
|
| 160 |
+ return &systemdCgroup{}, nil
|
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+func (c *systemdCgroup) Cleanup() error {
|
|
| 164 |
+ // systemd cleans up, we don't need to do anything |
|
| 165 |
+ return nil |
|
| 166 |
+} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"syscall" |
| 9 | 9 |
|
| 10 | 10 |
"github.com/dotcloud/docker/pkg/cgroups" |
| 11 |
+ "github.com/dotcloud/docker/pkg/cgroups/systemd" |
|
| 11 | 12 |
"github.com/dotcloud/docker/pkg/libcontainer" |
| 12 | 13 |
"github.com/dotcloud/docker/pkg/libcontainer/network" |
| 13 | 14 |
"github.com/dotcloud/docker/pkg/system" |
| ... | ... |
@@ -99,7 +100,11 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [ |
| 99 | 99 |
|
| 100 | 100 |
func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
|
| 101 | 101 |
if container.Cgroups != nil {
|
| 102 |
- return cgroups.Apply(container.Cgroups, nspid) |
|
| 102 |
+ c := container.Cgroups |
|
| 103 |
+ if systemd.UseSystemd() {
|
|
| 104 |
+ return systemd.Apply(c, nspid) |
|
| 105 |
+ } |
|
| 106 |
+ return rawApply(c, nspid) |
|
| 103 | 107 |
} |
| 104 | 108 |
return nil, nil |
| 105 | 109 |
} |