Signed-off-by: Alexander Morozov <lk4d4@docker.com>
| ... | ... |
@@ -14,6 +14,8 @@ import ( |
| 14 | 14 |
"syscall" |
| 15 | 15 |
"time" |
| 16 | 16 |
|
| 17 |
+ "github.com/docker/libcontainer" |
|
| 18 |
+ "github.com/docker/libcontainer/configs" |
|
| 17 | 19 |
"github.com/docker/libcontainer/devices" |
| 18 | 20 |
"github.com/docker/libcontainer/label" |
| 19 | 21 |
|
| ... | ... |
@@ -259,18 +261,18 @@ func populateCommand(c *Container, env []string) error {
|
| 259 | 259 |
pid.HostPid = c.hostConfig.PidMode.IsHost() |
| 260 | 260 |
|
| 261 | 261 |
// Build lists of devices allowed and created within the container. |
| 262 |
- userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices)) |
|
| 262 |
+ userSpecifiedDevices := make([]*configs.Device, len(c.hostConfig.Devices)) |
|
| 263 | 263 |
for i, deviceMapping := range c.hostConfig.Devices {
|
| 264 |
- device, err := devices.GetDevice(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) |
|
| 264 |
+ device, err := devices.DeviceFromPath(deviceMapping.PathOnHost, deviceMapping.CgroupPermissions) |
|
| 265 | 265 |
if err != nil {
|
| 266 | 266 |
return fmt.Errorf("error gathering device information while adding custom device %q: %s", deviceMapping.PathOnHost, err)
|
| 267 | 267 |
} |
| 268 | 268 |
device.Path = deviceMapping.PathInContainer |
| 269 | 269 |
userSpecifiedDevices[i] = device |
| 270 | 270 |
} |
| 271 |
- allowedDevices := append(devices.DefaultAllowedDevices, userSpecifiedDevices...) |
|
| 271 |
+ allowedDevices := append(configs.DefaultAllowedDevices, userSpecifiedDevices...) |
|
| 272 | 272 |
|
| 273 |
- autoCreatedDevices := append(devices.DefaultAutoCreatedDevices, userSpecifiedDevices...) |
|
| 273 |
+ autoCreatedDevices := append(configs.DefaultAutoCreatedDevices, userSpecifiedDevices...) |
|
| 274 | 274 |
|
| 275 | 275 |
// TODO: this can be removed after lxc-conf is fully deprecated |
| 276 | 276 |
lxcConfig, err := mergeLxcConfIntoOptions(c.hostConfig) |
| ... | ... |
@@ -972,7 +974,7 @@ func (container *Container) Exposes(p nat.Port) bool {
|
| 972 | 972 |
return exists |
| 973 | 973 |
} |
| 974 | 974 |
|
| 975 |
-func (container *Container) GetPtyMaster() (*os.File, error) {
|
|
| 975 |
+func (container *Container) GetPtyMaster() (libcontainer.Console, error) {
|
|
| 976 | 976 |
ttyConsole, ok := container.command.ProcessConfig.Terminal.(execdriver.TtyTerminal) |
| 977 | 977 |
if !ok {
|
| 978 | 978 |
return nil, ErrNoTTY |
| ... | ... |
@@ -1,17 +1,22 @@ |
| 1 | 1 |
package execdriver |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 4 | 5 |
"errors" |
| 5 | 6 |
"io" |
| 7 |
+ "io/ioutil" |
|
| 6 | 8 |
"os" |
| 7 | 9 |
"os/exec" |
| 10 |
+ "path/filepath" |
|
| 11 |
+ "strconv" |
|
| 8 | 12 |
"strings" |
| 9 | 13 |
"time" |
| 10 | 14 |
|
| 11 | 15 |
"github.com/docker/docker/daemon/execdriver/native/template" |
| 12 | 16 |
"github.com/docker/docker/pkg/ulimit" |
| 13 | 17 |
"github.com/docker/libcontainer" |
| 14 |
- "github.com/docker/libcontainer/devices" |
|
| 18 |
+ "github.com/docker/libcontainer/cgroups/fs" |
|
| 19 |
+ "github.com/docker/libcontainer/configs" |
|
| 15 | 20 |
) |
| 16 | 21 |
|
| 17 | 22 |
// Context is a generic key value pair that allows |
| ... | ... |
@@ -42,7 +47,7 @@ type Terminal interface {
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
type TtyTerminal interface {
|
| 45 |
- Master() *os.File |
|
| 45 |
+ Master() libcontainer.Console |
|
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 | 48 |
// ExitStatus provides exit reasons for a container. |
| ... | ... |
@@ -109,7 +114,7 @@ type Resources struct {
|
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 | 111 |
type ResourceStats struct {
|
| 112 |
- *libcontainer.ContainerStats |
|
| 112 |
+ *libcontainer.Stats |
|
| 113 | 113 |
Read time.Time `json:"read"` |
| 114 | 114 |
MemoryLimit int64 `json:"memory_limit"` |
| 115 | 115 |
SystemUsage uint64 `json:"system_usage"` |
| ... | ... |
@@ -149,8 +154,8 @@ type Command struct {
|
| 149 | 149 |
Pid *Pid `json:"pid"` |
| 150 | 150 |
Resources *Resources `json:"resources"` |
| 151 | 151 |
Mounts []Mount `json:"mounts"` |
| 152 |
- AllowedDevices []*devices.Device `json:"allowed_devices"` |
|
| 153 |
- AutoCreatedDevices []*devices.Device `json:"autocreated_devices"` |
|
| 152 |
+ AllowedDevices []*configs.Device `json:"allowed_devices"` |
|
| 153 |
+ AutoCreatedDevices []*configs.Device `json:"autocreated_devices"` |
|
| 154 | 154 |
CapAdd []string `json:"cap_add"` |
| 155 | 155 |
CapDrop []string `json:"cap_drop"` |
| 156 | 156 |
ContainerPid int `json:"container_pid"` // the pid for the process inside a container |
| ... | ... |
@@ -161,23 +166,19 @@ type Command struct {
|
| 161 | 161 |
AppArmorProfile string `json:"apparmor_profile"` |
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 |
-func InitContainer(c *Command) *libcontainer.Config {
|
|
| 164 |
+func InitContainer(c *Command) *configs.Config {
|
|
| 165 | 165 |
container := template.New() |
| 166 | 166 |
|
| 167 | 167 |
container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env)
|
| 168 |
- container.Tty = c.ProcessConfig.Tty |
|
| 169 |
- container.User = c.ProcessConfig.User |
|
| 170 |
- container.WorkingDir = c.WorkingDir |
|
| 171 |
- container.Env = c.ProcessConfig.Env |
|
| 172 | 168 |
container.Cgroups.Name = c.ID |
| 173 | 169 |
container.Cgroups.AllowedDevices = c.AllowedDevices |
| 174 |
- container.MountConfig.DeviceNodes = c.AutoCreatedDevices |
|
| 175 |
- container.RootFs = c.Rootfs |
|
| 176 |
- container.MountConfig.ReadonlyFs = c.ReadonlyRootfs |
|
| 170 |
+ container.Readonlyfs = c.ReadonlyRootfs |
|
| 171 |
+ container.Devices = c.AutoCreatedDevices |
|
| 172 |
+ container.Rootfs = c.Rootfs |
|
| 173 |
+ container.Readonlyfs = c.ReadonlyRootfs |
|
| 177 | 174 |
|
| 178 | 175 |
// check to see if we are running in ramdisk to disable pivot root |
| 179 |
- container.MountConfig.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
|
| 180 |
- container.RestrictSys = true |
|
| 176 |
+ container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
|
|
| 181 | 177 |
return container |
| 182 | 178 |
} |
| 183 | 179 |
|
| ... | ... |
@@ -191,7 +192,7 @@ func getEnv(key string, env []string) string {
|
| 191 | 191 |
return "" |
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 |
-func SetupCgroups(container *libcontainer.Config, c *Command) error {
|
|
| 194 |
+func SetupCgroups(container *configs.Config, c *Command) error {
|
|
| 195 | 195 |
if c.Resources != nil {
|
| 196 | 196 |
container.Cgroups.CpuShares = c.Resources.CpuShares |
| 197 | 197 |
container.Cgroups.Memory = c.Resources.Memory |
| ... | ... |
@@ -203,28 +204,98 @@ func SetupCgroups(container *libcontainer.Config, c *Command) error {
|
| 203 | 203 |
return nil |
| 204 | 204 |
} |
| 205 | 205 |
|
| 206 |
-func Stats(stateFile string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) {
|
|
| 207 |
- state, err := libcontainer.GetState(stateFile) |
|
| 208 |
- if err != nil {
|
|
| 209 |
- if os.IsNotExist(err) {
|
|
| 210 |
- return nil, ErrNotRunning |
|
| 206 |
+// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. |
|
| 207 |
+func getNetworkInterfaceStats(interfaceName string) (*libcontainer.NetworkInterface, error) {
|
|
| 208 |
+ out := &libcontainer.NetworkInterface{Name: interfaceName}
|
|
| 209 |
+ // This can happen if the network runtime information is missing - possible if the |
|
| 210 |
+ // container was created by an old version of libcontainer. |
|
| 211 |
+ if interfaceName == "" {
|
|
| 212 |
+ return out, nil |
|
| 213 |
+ } |
|
| 214 |
+ type netStatsPair struct {
|
|
| 215 |
+ // Where to write the output. |
|
| 216 |
+ Out *uint64 |
|
| 217 |
+ // The network stats file to read. |
|
| 218 |
+ File string |
|
| 219 |
+ } |
|
| 220 |
+ // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. |
|
| 221 |
+ netStats := []netStatsPair{
|
|
| 222 |
+ {Out: &out.RxBytes, File: "tx_bytes"},
|
|
| 223 |
+ {Out: &out.RxPackets, File: "tx_packets"},
|
|
| 224 |
+ {Out: &out.RxErrors, File: "tx_errors"},
|
|
| 225 |
+ {Out: &out.RxDropped, File: "tx_dropped"},
|
|
| 226 |
+ |
|
| 227 |
+ {Out: &out.TxBytes, File: "rx_bytes"},
|
|
| 228 |
+ {Out: &out.TxPackets, File: "rx_packets"},
|
|
| 229 |
+ {Out: &out.TxErrors, File: "rx_errors"},
|
|
| 230 |
+ {Out: &out.TxDropped, File: "rx_dropped"},
|
|
| 231 |
+ } |
|
| 232 |
+ for _, netStat := range netStats {
|
|
| 233 |
+ data, err := readSysfsNetworkStats(interfaceName, netStat.File) |
|
| 234 |
+ if err != nil {
|
|
| 235 |
+ return nil, err |
|
| 211 | 236 |
} |
| 237 |
+ *(netStat.Out) = data |
|
| 238 |
+ } |
|
| 239 |
+ return out, nil |
|
| 240 |
+} |
|
| 241 |
+ |
|
| 242 |
+// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics |
|
| 243 |
+func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
|
|
| 244 |
+ data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
|
|
| 245 |
+ if err != nil {
|
|
| 246 |
+ return 0, err |
|
| 247 |
+ } |
|
| 248 |
+ return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) |
|
| 249 |
+} |
|
| 250 |
+ |
|
| 251 |
+func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) {
|
|
| 252 |
+ f, err := os.Open(filepath.Join(containerDir, "state.json")) |
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ return nil, err |
|
| 255 |
+ } |
|
| 256 |
+ defer f.Close() |
|
| 257 |
+ |
|
| 258 |
+ type network struct {
|
|
| 259 |
+ Type string |
|
| 260 |
+ HostInterfaceName string |
|
| 261 |
+ } |
|
| 262 |
+ |
|
| 263 |
+ state := struct {
|
|
| 264 |
+ CgroupPaths map[string]string `json:"cgroup_paths"` |
|
| 265 |
+ Networks []network |
|
| 266 |
+ }{}
|
|
| 267 |
+ |
|
| 268 |
+ if err := json.NewDecoder(f).Decode(&state); err != nil {
|
|
| 212 | 269 |
return nil, err |
| 213 | 270 |
} |
| 214 | 271 |
now := time.Now() |
| 215 |
- stats, err := libcontainer.GetStats(nil, state) |
|
| 272 |
+ |
|
| 273 |
+ mgr := fs.Manager{Paths: state.CgroupPaths}
|
|
| 274 |
+ cstats, err := mgr.GetStats() |
|
| 216 | 275 |
if err != nil {
|
| 217 | 276 |
return nil, err |
| 218 | 277 |
} |
| 278 |
+ stats := &libcontainer.Stats{CgroupStats: cstats}
|
|
| 219 | 279 |
// if the container does not have any memory limit specified set the |
| 220 | 280 |
// limit to the machines memory |
| 221 | 281 |
memoryLimit := containerMemoryLimit |
| 222 | 282 |
if memoryLimit == 0 {
|
| 223 | 283 |
memoryLimit = machineMemory |
| 224 | 284 |
} |
| 285 |
+ for _, iface := range state.Networks {
|
|
| 286 |
+ switch iface.Type {
|
|
| 287 |
+ case "veth": |
|
| 288 |
+ istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) |
|
| 289 |
+ if err != nil {
|
|
| 290 |
+ return nil, err |
|
| 291 |
+ } |
|
| 292 |
+ stats.Interfaces = append(stats.Interfaces, istats) |
|
| 293 |
+ } |
|
| 294 |
+ } |
|
| 225 | 295 |
return &ResourceStats{
|
| 226 |
- Read: now, |
|
| 227 |
- ContainerStats: stats, |
|
| 228 |
- MemoryLimit: memoryLimit, |
|
| 296 |
+ Stats: stats, |
|
| 297 |
+ Read: now, |
|
| 298 |
+ MemoryLimit: memoryLimit, |
|
| 229 | 299 |
}, nil |
| 230 | 300 |
} |
| ... | ... |
@@ -23,7 +23,9 @@ import ( |
| 23 | 23 |
"github.com/docker/docker/utils" |
| 24 | 24 |
"github.com/docker/libcontainer" |
| 25 | 25 |
"github.com/docker/libcontainer/cgroups" |
| 26 |
- "github.com/docker/libcontainer/mount/nodes" |
|
| 26 |
+ "github.com/docker/libcontainer/configs" |
|
| 27 |
+ "github.com/docker/libcontainer/system" |
|
| 28 |
+ "github.com/docker/libcontainer/user" |
|
| 27 | 29 |
"github.com/kr/pty" |
| 28 | 30 |
) |
| 29 | 31 |
|
| ... | ... |
@@ -42,7 +44,7 @@ type driver struct {
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
type activeContainer struct {
|
| 45 |
- container *libcontainer.Config |
|
| 45 |
+ container *configs.Config |
|
| 46 | 46 |
cmd *exec.Cmd |
| 47 | 47 |
} |
| 48 | 48 |
|
| ... | ... |
@@ -190,7 +192,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 190 | 190 |
c.ProcessConfig.Path = aname |
| 191 | 191 |
c.ProcessConfig.Args = append([]string{name}, arg...)
|
| 192 | 192 |
|
| 193 |
- if err := nodes.CreateDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
|
|
| 193 |
+ if err := createDeviceNodes(c.Rootfs, c.AutoCreatedDevices); err != nil {
|
|
| 194 | 194 |
return execdriver.ExitStatus{ExitCode: -1}, err
|
| 195 | 195 |
} |
| 196 | 196 |
|
| ... | ... |
@@ -231,11 +233,17 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 231 | 231 |
} |
| 232 | 232 |
|
| 233 | 233 |
state := &libcontainer.State{
|
| 234 |
- InitPid: pid, |
|
| 235 |
- CgroupPaths: cgroupPaths, |
|
| 234 |
+ InitProcessPid: pid, |
|
| 235 |
+ CgroupPaths: cgroupPaths, |
|
| 236 | 236 |
} |
| 237 | 237 |
|
| 238 |
- if err := libcontainer.SaveState(dataPath, state); err != nil {
|
|
| 238 |
+ f, err := os.Create(filepath.Join(dataPath, "state.json")) |
|
| 239 |
+ if err != nil {
|
|
| 240 |
+ return terminate(err) |
|
| 241 |
+ } |
|
| 242 |
+ defer f.Close() |
|
| 243 |
+ |
|
| 244 |
+ if err := json.NewEncoder(f).Encode(state); err != nil {
|
|
| 239 | 245 |
return terminate(err) |
| 240 | 246 |
} |
| 241 | 247 |
|
| ... | ... |
@@ -245,8 +253,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 245 | 245 |
log.Debugf("Invoking startCallback")
|
| 246 | 246 |
startCallback(&c.ProcessConfig, pid) |
| 247 | 247 |
} |
| 248 |
+ |
|
| 248 | 249 |
oomKill := false |
| 249 |
- oomKillNotification, err := libcontainer.NotifyOnOOM(state) |
|
| 250 |
+ oomKillNotification, err := notifyOnOOM(cgroupPaths) |
|
| 250 | 251 |
if err == nil {
|
| 251 | 252 |
_, oomKill = <-oomKillNotification |
| 252 | 253 |
log.Debugf("oomKill error %s waitErr %s", oomKill, waitErr)
|
| ... | ... |
@@ -265,9 +274,57 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 265 | 265 |
return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr
|
| 266 | 266 |
} |
| 267 | 267 |
|
| 268 |
+// copy from libcontainer |
|
| 269 |
+func notifyOnOOM(paths map[string]string) (<-chan struct{}, error) {
|
|
| 270 |
+ dir := paths["memory"] |
|
| 271 |
+ if dir == "" {
|
|
| 272 |
+ return nil, fmt.Errorf("There is no path for %q in state", "memory")
|
|
| 273 |
+ } |
|
| 274 |
+ oomControl, err := os.Open(filepath.Join(dir, "memory.oom_control")) |
|
| 275 |
+ if err != nil {
|
|
| 276 |
+ return nil, err |
|
| 277 |
+ } |
|
| 278 |
+ fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0) |
|
| 279 |
+ if syserr != 0 {
|
|
| 280 |
+ oomControl.Close() |
|
| 281 |
+ return nil, syserr |
|
| 282 |
+ } |
|
| 283 |
+ |
|
| 284 |
+ eventfd := os.NewFile(fd, "eventfd") |
|
| 285 |
+ |
|
| 286 |
+ eventControlPath := filepath.Join(dir, "cgroup.event_control") |
|
| 287 |
+ data := fmt.Sprintf("%d %d", eventfd.Fd(), oomControl.Fd())
|
|
| 288 |
+ if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
|
|
| 289 |
+ eventfd.Close() |
|
| 290 |
+ oomControl.Close() |
|
| 291 |
+ return nil, err |
|
| 292 |
+ } |
|
| 293 |
+ ch := make(chan struct{})
|
|
| 294 |
+ go func() {
|
|
| 295 |
+ defer func() {
|
|
| 296 |
+ close(ch) |
|
| 297 |
+ eventfd.Close() |
|
| 298 |
+ oomControl.Close() |
|
| 299 |
+ }() |
|
| 300 |
+ buf := make([]byte, 8) |
|
| 301 |
+ for {
|
|
| 302 |
+ if _, err := eventfd.Read(buf); err != nil {
|
|
| 303 |
+ return |
|
| 304 |
+ } |
|
| 305 |
+ // When a cgroup is destroyed, an event is sent to eventfd. |
|
| 306 |
+ // So if the control path is gone, return instead of notifying. |
|
| 307 |
+ if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
|
|
| 308 |
+ return |
|
| 309 |
+ } |
|
| 310 |
+ ch <- struct{}{}
|
|
| 311 |
+ } |
|
| 312 |
+ }() |
|
| 313 |
+ return ch, nil |
|
| 314 |
+} |
|
| 315 |
+ |
|
| 268 | 316 |
// createContainer populates and configures the container type with the |
| 269 | 317 |
// data provided by the execdriver.Command |
| 270 |
-func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, error) {
|
|
| 318 |
+func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
|
| 271 | 319 |
container := execdriver.InitContainer(c) |
| 272 | 320 |
if err := execdriver.SetupCgroups(container, c); err != nil {
|
| 273 | 321 |
return nil, err |
| ... | ... |
@@ -297,6 +354,87 @@ func cgroupPaths(containerId string) (map[string]string, error) {
|
| 297 | 297 |
return paths, nil |
| 298 | 298 |
} |
| 299 | 299 |
|
| 300 |
+// this is copy from old libcontainer nodes.go |
|
| 301 |
+func createDeviceNodes(rootfs string, nodesToCreate []*configs.Device) error {
|
|
| 302 |
+ oldMask := syscall.Umask(0000) |
|
| 303 |
+ defer syscall.Umask(oldMask) |
|
| 304 |
+ |
|
| 305 |
+ for _, node := range nodesToCreate {
|
|
| 306 |
+ if err := createDeviceNode(rootfs, node); err != nil {
|
|
| 307 |
+ return err |
|
| 308 |
+ } |
|
| 309 |
+ } |
|
| 310 |
+ return nil |
|
| 311 |
+} |
|
| 312 |
+ |
|
| 313 |
+// Creates the device node in the rootfs of the container. |
|
| 314 |
+func createDeviceNode(rootfs string, node *configs.Device) error {
|
|
| 315 |
+ var ( |
|
| 316 |
+ dest = filepath.Join(rootfs, node.Path) |
|
| 317 |
+ parent = filepath.Dir(dest) |
|
| 318 |
+ ) |
|
| 319 |
+ |
|
| 320 |
+ if err := os.MkdirAll(parent, 0755); err != nil {
|
|
| 321 |
+ return err |
|
| 322 |
+ } |
|
| 323 |
+ |
|
| 324 |
+ fileMode := node.FileMode |
|
| 325 |
+ switch node.Type {
|
|
| 326 |
+ case 'c': |
|
| 327 |
+ fileMode |= syscall.S_IFCHR |
|
| 328 |
+ case 'b': |
|
| 329 |
+ fileMode |= syscall.S_IFBLK |
|
| 330 |
+ default: |
|
| 331 |
+ return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
|
|
| 332 |
+ } |
|
| 333 |
+ |
|
| 334 |
+ if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil && !os.IsExist(err) {
|
|
| 335 |
+ return fmt.Errorf("mknod %s %s", node.Path, err)
|
|
| 336 |
+ } |
|
| 337 |
+ |
|
| 338 |
+ if err := syscall.Chown(dest, int(node.Uid), int(node.Gid)); err != nil {
|
|
| 339 |
+ return fmt.Errorf("chown %s to %d:%d", node.Path, node.Uid, node.Gid)
|
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ return nil |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+// setupUser changes the groups, gid, and uid for the user inside the container |
|
| 346 |
+// copy from libcontainer, cause not it's private |
|
| 347 |
+func setupUser(userSpec string) error {
|
|
| 348 |
+ // Set up defaults. |
|
| 349 |
+ defaultExecUser := user.ExecUser{
|
|
| 350 |
+ Uid: syscall.Getuid(), |
|
| 351 |
+ Gid: syscall.Getgid(), |
|
| 352 |
+ Home: "/", |
|
| 353 |
+ } |
|
| 354 |
+ passwdPath, err := user.GetPasswdPath() |
|
| 355 |
+ if err != nil {
|
|
| 356 |
+ return err |
|
| 357 |
+ } |
|
| 358 |
+ groupPath, err := user.GetGroupPath() |
|
| 359 |
+ if err != nil {
|
|
| 360 |
+ return err |
|
| 361 |
+ } |
|
| 362 |
+ execUser, err := user.GetExecUserPath(userSpec, &defaultExecUser, passwdPath, groupPath) |
|
| 363 |
+ if err != nil {
|
|
| 364 |
+ return err |
|
| 365 |
+ } |
|
| 366 |
+ if err := system.Setgid(execUser.Gid); err != nil {
|
|
| 367 |
+ return err |
|
| 368 |
+ } |
|
| 369 |
+ if err := system.Setuid(execUser.Uid); err != nil {
|
|
| 370 |
+ return err |
|
| 371 |
+ } |
|
| 372 |
+ // if we didn't get HOME already, set it based on the user's HOME |
|
| 373 |
+ if envHome := os.Getenv("HOME"); envHome == "" {
|
|
| 374 |
+ if err := os.Setenv("HOME", execUser.Home); err != nil {
|
|
| 375 |
+ return err |
|
| 376 |
+ } |
|
| 377 |
+ } |
|
| 378 |
+ return nil |
|
| 379 |
+} |
|
| 380 |
+ |
|
| 300 | 381 |
/// Return the exit code of the process |
| 301 | 382 |
// if the process has not exited -1 will be returned |
| 302 | 383 |
func getExitCode(c *execdriver.Command) int {
|
| ... | ... |
@@ -3,8 +3,6 @@ package lxc |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
- "github.com/docker/libcontainer" |
|
| 7 |
- "github.com/docker/libcontainer/namespaces" |
|
| 8 | 6 |
"github.com/docker/libcontainer/utils" |
| 9 | 7 |
) |
| 10 | 8 |
|
| ... | ... |
@@ -12,9 +10,7 @@ func finalizeNamespace(args *InitArgs) error {
|
| 12 | 12 |
if err := utils.CloseExecFrom(3); err != nil {
|
| 13 | 13 |
return err |
| 14 | 14 |
} |
| 15 |
- if err := namespaces.SetupUser(&libcontainer.Config{
|
|
| 16 |
- User: args.User, |
|
| 17 |
- }); err != nil {
|
|
| 15 |
+ if err := setupUser(args.User); err != nil {
|
|
| 18 | 16 |
return fmt.Errorf("setup user %s", err)
|
| 19 | 17 |
} |
| 20 | 18 |
if err := setupWorkingDirectory(args); err != nil {
|
| ... | ... |
@@ -11,7 +11,6 @@ import ( |
| 11 | 11 |
nativeTemplate "github.com/docker/docker/daemon/execdriver/native/template" |
| 12 | 12 |
"github.com/docker/docker/utils" |
| 13 | 13 |
"github.com/docker/libcontainer/label" |
| 14 |
- "github.com/docker/libcontainer/security/capabilities" |
|
| 15 | 14 |
) |
| 16 | 15 |
|
| 17 | 16 |
const LxcTemplate = ` |
| ... | ... |
@@ -169,7 +168,7 @@ func keepCapabilities(adds []string, drops []string) ([]string, error) {
|
| 169 | 169 |
var newCaps []string |
| 170 | 170 |
for _, cap := range caps {
|
| 171 | 171 |
log.Debugf("cap %s\n", cap)
|
| 172 |
- realCap := capabilities.GetCapability(cap) |
|
| 172 |
+ realCap := execdriver.GetCapability(cap) |
|
| 173 | 173 |
numCap := fmt.Sprintf("%d", realCap.Value)
|
| 174 | 174 |
newCaps = append(newCaps, numCap) |
| 175 | 175 |
} |
| ... | ... |
@@ -180,13 +179,10 @@ func keepCapabilities(adds []string, drops []string) ([]string, error) {
|
| 180 | 180 |
func dropList(drops []string) ([]string, error) {
|
| 181 | 181 |
if utils.StringsContainsNoCase(drops, "all") {
|
| 182 | 182 |
var newCaps []string |
| 183 |
- for _, cap := range capabilities.GetAllCapabilities() {
|
|
| 184 |
- log.Debugf("drop cap %s\n", cap)
|
|
| 185 |
- realCap := capabilities.GetCapability(cap) |
|
| 186 |
- if realCap == nil {
|
|
| 187 |
- return nil, fmt.Errorf("Invalid capability '%s'", cap)
|
|
| 188 |
- } |
|
| 189 |
- numCap := fmt.Sprintf("%d", realCap.Value)
|
|
| 183 |
+ for _, capName := range execdriver.GetAllCapabilities() {
|
|
| 184 |
+ cap := execdriver.GetCapability(capName) |
|
| 185 |
+ log.Debugf("drop cap %s\n", cap.Key)
|
|
| 186 |
+ numCap := fmt.Sprintf("%d", cap.Value)
|
|
| 190 | 187 |
newCaps = append(newCaps, numCap) |
| 191 | 188 |
} |
| 192 | 189 |
return newCaps, nil |
| ... | ... |
@@ -5,11 +5,6 @@ package lxc |
| 5 | 5 |
import ( |
| 6 | 6 |
"bufio" |
| 7 | 7 |
"fmt" |
| 8 |
- "github.com/docker/docker/daemon/execdriver" |
|
| 9 |
- nativeTemplate "github.com/docker/docker/daemon/execdriver/native/template" |
|
| 10 |
- "github.com/docker/libcontainer/devices" |
|
| 11 |
- "github.com/docker/libcontainer/security/capabilities" |
|
| 12 |
- "github.com/syndtr/gocapability/capability" |
|
| 13 | 8 |
"io/ioutil" |
| 14 | 9 |
"math/rand" |
| 15 | 10 |
"os" |
| ... | ... |
@@ -17,6 +12,11 @@ import ( |
| 17 | 17 |
"strings" |
| 18 | 18 |
"testing" |
| 19 | 19 |
"time" |
| 20 |
+ |
|
| 21 |
+ "github.com/docker/docker/daemon/execdriver" |
|
| 22 |
+ nativeTemplate "github.com/docker/docker/daemon/execdriver/native/template" |
|
| 23 |
+ "github.com/docker/libcontainer/configs" |
|
| 24 |
+ "github.com/syndtr/gocapability/capability" |
|
| 20 | 25 |
) |
| 21 | 26 |
|
| 22 | 27 |
func TestLXCConfig(t *testing.T) {
|
| ... | ... |
@@ -53,7 +53,7 @@ func TestLXCConfig(t *testing.T) {
|
| 53 | 53 |
Mtu: 1500, |
| 54 | 54 |
Interface: nil, |
| 55 | 55 |
}, |
| 56 |
- AllowedDevices: make([]*devices.Device, 0), |
|
| 56 |
+ AllowedDevices: make([]*configs.Device, 0), |
|
| 57 | 57 |
ProcessConfig: execdriver.ProcessConfig{},
|
| 58 | 58 |
} |
| 59 | 59 |
p, err := driver.generateLXCConfig(command) |
| ... | ... |
@@ -295,7 +295,7 @@ func TestCustomLxcConfigMisc(t *testing.T) {
|
| 295 | 295 |
grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") |
| 296 | 296 |
container := nativeTemplate.New() |
| 297 | 297 |
for _, cap := range container.Capabilities {
|
| 298 |
- realCap := capabilities.GetCapability(cap) |
|
| 298 |
+ realCap := execdriver.GetCapability(cap) |
|
| 299 | 299 |
numCap := fmt.Sprintf("%d", realCap.Value)
|
| 300 | 300 |
if cap != "MKNOD" && cap != "KILL" {
|
| 301 | 301 |
grepFile(t, p, fmt.Sprintf("lxc.cap.keep = %s", numCap))
|
| ... | ... |
@@ -359,7 +359,7 @@ func TestCustomLxcConfigMiscOverride(t *testing.T) {
|
| 359 | 359 |
grepFile(t, p, "lxc.cgroup.cpuset.cpus = 0,1") |
| 360 | 360 |
container := nativeTemplate.New() |
| 361 | 361 |
for _, cap := range container.Capabilities {
|
| 362 |
- realCap := capabilities.GetCapability(cap) |
|
| 362 |
+ realCap := execdriver.GetCapability(cap) |
|
| 363 | 363 |
numCap := fmt.Sprintf("%d", realCap.Value)
|
| 364 | 364 |
if cap != "MKNOD" && cap != "KILL" {
|
| 365 | 365 |
grepFile(t, p, fmt.Sprintf("lxc.cap.keep = %s", numCap))
|
| ... | ... |
@@ -3,21 +3,24 @@ |
| 3 | 3 |
package native |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "errors" |
|
| 6 | 7 |
"fmt" |
| 7 |
- "os/exec" |
|
| 8 |
+ "net" |
|
| 8 | 9 |
"path/filepath" |
| 10 |
+ "strings" |
|
| 11 |
+ "syscall" |
|
| 9 | 12 |
|
| 10 | 13 |
"github.com/docker/docker/daemon/execdriver" |
| 11 |
- "github.com/docker/libcontainer" |
|
| 14 |
+ "github.com/docker/docker/pkg/symlink" |
|
| 12 | 15 |
"github.com/docker/libcontainer/apparmor" |
| 16 |
+ "github.com/docker/libcontainer/configs" |
|
| 13 | 17 |
"github.com/docker/libcontainer/devices" |
| 14 |
- "github.com/docker/libcontainer/mount" |
|
| 15 |
- "github.com/docker/libcontainer/security/capabilities" |
|
| 18 |
+ "github.com/docker/libcontainer/utils" |
|
| 16 | 19 |
) |
| 17 | 20 |
|
| 18 | 21 |
// createContainer populates and configures the container type with the |
| 19 | 22 |
// data provided by the execdriver.Command |
| 20 |
-func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, error) {
|
|
| 23 |
+func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
|
| 21 | 24 |
container := execdriver.InitContainer(c) |
| 22 | 25 |
|
| 23 | 26 |
if err := d.createIpc(container, c); err != nil {
|
| ... | ... |
@@ -33,6 +36,13 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e |
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 | 35 |
if c.ProcessConfig.Privileged {
|
| 36 |
+ // clear readonly for /sys |
|
| 37 |
+ for i := range container.Mounts {
|
|
| 38 |
+ if container.Mounts[i].Destination == "/sys" {
|
|
| 39 |
+ container.Mounts[i].Flags &= ^syscall.MS_RDONLY |
|
| 40 |
+ } |
|
| 41 |
+ } |
|
| 42 |
+ container.ReadonlyPaths = nil |
|
| 36 | 43 |
if err := d.setPrivileged(container); err != nil {
|
| 37 | 44 |
return nil, err |
| 38 | 45 |
} |
| ... | ... |
@@ -57,43 +67,52 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e |
| 57 | 57 |
if err := d.setupLabels(container, c); err != nil {
|
| 58 | 58 |
return nil, err |
| 59 | 59 |
} |
| 60 |
- |
|
| 61 | 60 |
d.setupRlimits(container, c) |
| 61 |
+ return container, nil |
|
| 62 |
+} |
|
| 62 | 63 |
|
| 63 |
- cmds := make(map[string]*exec.Cmd) |
|
| 64 |
- d.Lock() |
|
| 65 |
- for k, v := range d.activeContainers {
|
|
| 66 |
- cmds[k] = v.cmd |
|
| 64 |
+func generateIfaceName() (string, error) {
|
|
| 65 |
+ for i := 0; i < 10; i++ {
|
|
| 66 |
+ name, err := utils.GenerateRandomName("veth", 7)
|
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ continue |
|
| 69 |
+ } |
|
| 70 |
+ if _, err := net.InterfaceByName(name); err != nil {
|
|
| 71 |
+ if strings.Contains(err.Error(), "no such") {
|
|
| 72 |
+ return name, nil |
|
| 73 |
+ } |
|
| 74 |
+ return "", err |
|
| 75 |
+ } |
|
| 67 | 76 |
} |
| 68 |
- d.Unlock() |
|
| 69 |
- |
|
| 70 |
- return container, nil |
|
| 77 |
+ return "", errors.New("Failed to find name for new interface")
|
|
| 71 | 78 |
} |
| 72 | 79 |
|
| 73 |
-func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Command) error {
|
|
| 80 |
+func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) error {
|
|
| 74 | 81 |
if c.Network.HostNetworking {
|
| 75 |
- container.Namespaces.Remove(libcontainer.NEWNET) |
|
| 82 |
+ container.Namespaces.Remove(configs.NEWNET) |
|
| 76 | 83 |
return nil |
| 77 | 84 |
} |
| 78 | 85 |
|
| 79 |
- container.Networks = []*libcontainer.Network{
|
|
| 86 |
+ container.Networks = []*configs.Network{
|
|
| 80 | 87 |
{
|
| 81 |
- Mtu: c.Network.Mtu, |
|
| 82 |
- Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
|
|
| 83 |
- Gateway: "localhost", |
|
| 84 |
- Type: "loopback", |
|
| 88 |
+ Type: "loopback", |
|
| 85 | 89 |
}, |
| 86 | 90 |
} |
| 87 | 91 |
|
| 92 |
+ iName, err := generateIfaceName() |
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ return err |
|
| 95 |
+ } |
|
| 88 | 96 |
if c.Network.Interface != nil {
|
| 89 |
- vethNetwork := libcontainer.Network{
|
|
| 90 |
- Mtu: c.Network.Mtu, |
|
| 91 |
- Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
|
|
| 92 |
- MacAddress: c.Network.Interface.MacAddress, |
|
| 93 |
- Gateway: c.Network.Interface.Gateway, |
|
| 94 |
- Type: "veth", |
|
| 95 |
- Bridge: c.Network.Interface.Bridge, |
|
| 96 |
- VethPrefix: "veth", |
|
| 97 |
+ vethNetwork := configs.Network{
|
|
| 98 |
+ Name: "eth0", |
|
| 99 |
+ HostInterfaceName: iName, |
|
| 100 |
+ Mtu: c.Network.Mtu, |
|
| 101 |
+ Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
|
|
| 102 |
+ MacAddress: c.Network.Interface.MacAddress, |
|
| 103 |
+ Gateway: c.Network.Interface.Gateway, |
|
| 104 |
+ Type: "veth", |
|
| 105 |
+ Bridge: c.Network.Interface.Bridge, |
|
| 97 | 106 |
} |
| 98 | 107 |
if c.Network.Interface.GlobalIPv6Address != "" {
|
| 99 | 108 |
vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen)
|
| ... | ... |
@@ -107,21 +126,24 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com |
| 107 | 107 |
active := d.activeContainers[c.Network.ContainerID] |
| 108 | 108 |
d.Unlock() |
| 109 | 109 |
|
| 110 |
- if active == nil || active.cmd.Process == nil {
|
|
| 110 |
+ if active == nil {
|
|
| 111 | 111 |
return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
|
| 112 | 112 |
} |
| 113 |
- cmd := active.cmd |
|
| 114 | 113 |
|
| 115 |
- nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
|
|
| 116 |
- container.Namespaces.Add(libcontainer.NEWNET, nspath) |
|
| 114 |
+ state, err := active.State() |
|
| 115 |
+ if err != nil {
|
|
| 116 |
+ return err |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ container.Namespaces.Add(configs.NEWNET, state.NamespacePaths[configs.NEWNET]) |
|
| 117 | 120 |
} |
| 118 | 121 |
|
| 119 | 122 |
return nil |
| 120 | 123 |
} |
| 121 | 124 |
|
| 122 |
-func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command) error {
|
|
| 125 |
+func (d *driver) createIpc(container *configs.Config, c *execdriver.Command) error {
|
|
| 123 | 126 |
if c.Ipc.HostIpc {
|
| 124 |
- container.Namespaces.Remove(libcontainer.NEWIPC) |
|
| 127 |
+ container.Namespaces.Remove(configs.NEWIPC) |
|
| 125 | 128 |
return nil |
| 126 | 129 |
} |
| 127 | 130 |
|
| ... | ... |
@@ -130,37 +152,38 @@ func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command |
| 130 | 130 |
active := d.activeContainers[c.Ipc.ContainerID] |
| 131 | 131 |
d.Unlock() |
| 132 | 132 |
|
| 133 |
- if active == nil || active.cmd.Process == nil {
|
|
| 133 |
+ if active == nil {
|
|
| 134 | 134 |
return fmt.Errorf("%s is not a valid running container to join", c.Ipc.ContainerID)
|
| 135 | 135 |
} |
| 136 |
- cmd := active.cmd |
|
| 137 | 136 |
|
| 138 |
- container.Namespaces.Add(libcontainer.NEWIPC, filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "ipc"))
|
|
| 137 |
+ state, err := active.State() |
|
| 138 |
+ if err != nil {
|
|
| 139 |
+ return err |
|
| 140 |
+ } |
|
| 141 |
+ container.Namespaces.Add(configs.NEWIPC, state.NamespacePaths[configs.NEWIPC]) |
|
| 139 | 142 |
} |
| 140 | 143 |
|
| 141 | 144 |
return nil |
| 142 | 145 |
} |
| 143 | 146 |
|
| 144 |
-func (d *driver) createPid(container *libcontainer.Config, c *execdriver.Command) error {
|
|
| 147 |
+func (d *driver) createPid(container *configs.Config, c *execdriver.Command) error {
|
|
| 145 | 148 |
if c.Pid.HostPid {
|
| 146 |
- container.Namespaces.Remove(libcontainer.NEWPID) |
|
| 149 |
+ container.Namespaces.Remove(configs.NEWPID) |
|
| 147 | 150 |
return nil |
| 148 | 151 |
} |
| 149 | 152 |
|
| 150 | 153 |
return nil |
| 151 | 154 |
} |
| 152 | 155 |
|
| 153 |
-func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
|
|
| 154 |
- container.Capabilities = capabilities.GetAllCapabilities() |
|
| 156 |
+func (d *driver) setPrivileged(container *configs.Config) (err error) {
|
|
| 157 |
+ container.Capabilities = execdriver.GetAllCapabilities() |
|
| 155 | 158 |
container.Cgroups.AllowAllDevices = true |
| 156 | 159 |
|
| 157 |
- hostDeviceNodes, err := devices.GetHostDeviceNodes() |
|
| 160 |
+ hostDevices, err := devices.HostDevices() |
|
| 158 | 161 |
if err != nil {
|
| 159 | 162 |
return err |
| 160 | 163 |
} |
| 161 |
- container.MountConfig.DeviceNodes = hostDeviceNodes |
|
| 162 |
- |
|
| 163 |
- container.RestrictSys = false |
|
| 164 |
+ container.Devices = hostDevices |
|
| 164 | 165 |
|
| 165 | 166 |
if apparmor.IsEnabled() {
|
| 166 | 167 |
container.AppArmorProfile = "unconfined" |
| ... | ... |
@@ -169,39 +192,52 @@ func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
|
| 169 | 169 |
return nil |
| 170 | 170 |
} |
| 171 | 171 |
|
| 172 |
-func (d *driver) setCapabilities(container *libcontainer.Config, c *execdriver.Command) (err error) {
|
|
| 172 |
+func (d *driver) setCapabilities(container *configs.Config, c *execdriver.Command) (err error) {
|
|
| 173 | 173 |
container.Capabilities, err = execdriver.TweakCapabilities(container.Capabilities, c.CapAdd, c.CapDrop) |
| 174 | 174 |
return err |
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 |
-func (d *driver) setupRlimits(container *libcontainer.Config, c *execdriver.Command) {
|
|
| 177 |
+func (d *driver) setupRlimits(container *configs.Config, c *execdriver.Command) {
|
|
| 178 | 178 |
if c.Resources == nil {
|
| 179 | 179 |
return |
| 180 | 180 |
} |
| 181 | 181 |
|
| 182 | 182 |
for _, rlimit := range c.Resources.Rlimits {
|
| 183 |
- container.Rlimits = append(container.Rlimits, libcontainer.Rlimit((*rlimit))) |
|
| 183 |
+ container.Rlimits = append(container.Rlimits, configs.Rlimit{
|
|
| 184 |
+ Type: rlimit.Type, |
|
| 185 |
+ Hard: rlimit.Hard, |
|
| 186 |
+ Soft: rlimit.Soft, |
|
| 187 |
+ }) |
|
| 184 | 188 |
} |
| 185 | 189 |
} |
| 186 | 190 |
|
| 187 |
-func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Command) error {
|
|
| 191 |
+func (d *driver) setupMounts(container *configs.Config, c *execdriver.Command) error {
|
|
| 188 | 192 |
for _, m := range c.Mounts {
|
| 189 |
- container.MountConfig.Mounts = append(container.MountConfig.Mounts, &mount.Mount{
|
|
| 190 |
- Type: "bind", |
|
| 193 |
+ dest, err := symlink.FollowSymlinkInScope(filepath.Join(c.Rootfs, m.Destination), c.Rootfs) |
|
| 194 |
+ if err != nil {
|
|
| 195 |
+ return err |
|
| 196 |
+ } |
|
| 197 |
+ flags := syscall.MS_BIND | syscall.MS_REC |
|
| 198 |
+ if !m.Writable {
|
|
| 199 |
+ flags |= syscall.MS_RDONLY |
|
| 200 |
+ } |
|
| 201 |
+ if m.Slave {
|
|
| 202 |
+ flags |= syscall.MS_SLAVE |
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ container.Mounts = append(container.Mounts, &configs.Mount{
|
|
| 191 | 206 |
Source: m.Source, |
| 192 |
- Destination: m.Destination, |
|
| 193 |
- Writable: m.Writable, |
|
| 194 |
- Private: m.Private, |
|
| 195 |
- Slave: m.Slave, |
|
| 207 |
+ Destination: dest, |
|
| 208 |
+ Device: "bind", |
|
| 209 |
+ Flags: flags, |
|
| 196 | 210 |
}) |
| 197 | 211 |
} |
| 198 |
- |
|
| 199 | 212 |
return nil |
| 200 | 213 |
} |
| 201 | 214 |
|
| 202 |
-func (d *driver) setupLabels(container *libcontainer.Config, c *execdriver.Command) error {
|
|
| 215 |
+func (d *driver) setupLabels(container *configs.Config, c *execdriver.Command) error {
|
|
| 203 | 216 |
container.ProcessLabel = c.ProcessLabel |
| 204 |
- container.MountConfig.MountLabel = c.MountLabel |
|
| 217 |
+ container.MountLabel = c.MountLabel |
|
| 205 | 218 |
|
| 206 | 219 |
return nil |
| 207 | 220 |
} |
| ... | ... |
@@ -4,28 +4,28 @@ package native |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"encoding/json" |
| 7 |
- "errors" |
|
| 8 | 7 |
"fmt" |
| 9 | 8 |
"io" |
| 10 | 9 |
"io/ioutil" |
| 11 | 10 |
"os" |
| 12 | 11 |
"os/exec" |
| 13 | 12 |
"path/filepath" |
| 13 |
+ "strings" |
|
| 14 | 14 |
"sync" |
| 15 | 15 |
"syscall" |
| 16 |
+ "time" |
|
| 16 | 17 |
|
| 17 | 18 |
log "github.com/Sirupsen/logrus" |
| 18 | 19 |
"github.com/docker/docker/daemon/execdriver" |
| 20 |
+ "github.com/docker/docker/pkg/reexec" |
|
| 19 | 21 |
sysinfo "github.com/docker/docker/pkg/system" |
| 20 | 22 |
"github.com/docker/docker/pkg/term" |
| 21 | 23 |
"github.com/docker/libcontainer" |
| 22 | 24 |
"github.com/docker/libcontainer/apparmor" |
| 23 |
- "github.com/docker/libcontainer/cgroups/fs" |
|
| 24 | 25 |
"github.com/docker/libcontainer/cgroups/systemd" |
| 25 |
- consolepkg "github.com/docker/libcontainer/console" |
|
| 26 |
- "github.com/docker/libcontainer/namespaces" |
|
| 27 |
- _ "github.com/docker/libcontainer/namespaces/nsenter" |
|
| 26 |
+ "github.com/docker/libcontainer/configs" |
|
| 28 | 27 |
"github.com/docker/libcontainer/system" |
| 28 |
+ "github.com/docker/libcontainer/utils" |
|
| 29 | 29 |
) |
| 30 | 30 |
|
| 31 | 31 |
const ( |
| ... | ... |
@@ -33,16 +33,12 @@ const ( |
| 33 | 33 |
Version = "0.2" |
| 34 | 34 |
) |
| 35 | 35 |
|
| 36 |
-type activeContainer struct {
|
|
| 37 |
- container *libcontainer.Config |
|
| 38 |
- cmd *exec.Cmd |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 | 36 |
type driver struct {
|
| 42 | 37 |
root string |
| 43 | 38 |
initPath string |
| 44 |
- activeContainers map[string]*activeContainer |
|
| 39 |
+ activeContainers map[string]libcontainer.Container |
|
| 45 | 40 |
machineMemory int64 |
| 41 |
+ factory libcontainer.Factory |
|
| 46 | 42 |
sync.Mutex |
| 47 | 43 |
} |
| 48 | 44 |
|
| ... | ... |
@@ -59,11 +55,22 @@ func NewDriver(root, initPath string) (*driver, error) {
|
| 59 | 59 |
if err := apparmor.InstallDefaultProfile(); err != nil {
|
| 60 | 60 |
return nil, err |
| 61 | 61 |
} |
| 62 |
+ cgm := libcontainer.Cgroupfs |
|
| 63 |
+ if systemd.UseSystemd() {
|
|
| 64 |
+ cgm = libcontainer.SystemdCgroups |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ f, err := libcontainer.New(root, cgm, libcontainer.InitPath(reexec.Self(), DriverName)) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return nil, err |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 62 | 72 |
return &driver{
|
| 63 | 73 |
root: root, |
| 64 | 74 |
initPath: initPath, |
| 65 |
- activeContainers: make(map[string]*activeContainer), |
|
| 75 |
+ activeContainers: make(map[string]libcontainer.Container), |
|
| 66 | 76 |
machineMemory: meminfo.MemTotal, |
| 77 |
+ factory: f, |
|
| 67 | 78 |
}, nil |
| 68 | 79 |
} |
| 69 | 80 |
|
| ... | ... |
@@ -81,101 +88,141 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba |
| 81 | 81 |
|
| 82 | 82 |
var term execdriver.Terminal |
| 83 | 83 |
|
| 84 |
+ p := &libcontainer.Process{
|
|
| 85 |
+ Args: append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...),
|
|
| 86 |
+ Env: c.ProcessConfig.Env, |
|
| 87 |
+ Cwd: c.WorkingDir, |
|
| 88 |
+ User: c.ProcessConfig.User, |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 84 | 91 |
if c.ProcessConfig.Tty {
|
| 85 |
- term, err = NewTtyConsole(&c.ProcessConfig, pipes) |
|
| 92 |
+ rootuid, err := container.HostUID() |
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 95 |
+ } |
|
| 96 |
+ cons, err := p.NewConsole(rootuid) |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 99 |
+ } |
|
| 100 |
+ term, err = NewTtyConsole(cons, pipes, rootuid) |
|
| 86 | 101 |
} else {
|
| 87 |
- term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes) |
|
| 102 |
+ p.Stdout = pipes.Stdout |
|
| 103 |
+ p.Stderr = pipes.Stderr |
|
| 104 |
+ r, w, err := os.Pipe() |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 107 |
+ } |
|
| 108 |
+ if pipes.Stdin != nil {
|
|
| 109 |
+ go func() {
|
|
| 110 |
+ io.Copy(w, pipes.Stdin) |
|
| 111 |
+ w.Close() |
|
| 112 |
+ }() |
|
| 113 |
+ p.Stdin = r |
|
| 114 |
+ } |
|
| 115 |
+ term = &execdriver.StdConsole{}
|
|
| 88 | 116 |
} |
| 89 | 117 |
if err != nil {
|
| 90 | 118 |
return execdriver.ExitStatus{ExitCode: -1}, err
|
| 91 | 119 |
} |
| 92 | 120 |
c.ProcessConfig.Terminal = term |
| 93 | 121 |
|
| 94 |
- d.Lock() |
|
| 95 |
- d.activeContainers[c.ID] = &activeContainer{
|
|
| 96 |
- container: container, |
|
| 97 |
- cmd: &c.ProcessConfig.Cmd, |
|
| 122 |
+ cont, err := d.factory.Create(c.ID, container) |
|
| 123 |
+ if err != nil {
|
|
| 124 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 98 | 125 |
} |
| 126 |
+ d.Lock() |
|
| 127 |
+ d.activeContainers[c.ID] = cont |
|
| 99 | 128 |
d.Unlock() |
| 129 |
+ defer func() {
|
|
| 130 |
+ cont.Destroy() |
|
| 131 |
+ d.cleanContainer(c.ID) |
|
| 132 |
+ }() |
|
| 100 | 133 |
|
| 101 |
- var ( |
|
| 102 |
- dataPath = filepath.Join(d.root, c.ID) |
|
| 103 |
- args = append([]string{c.ProcessConfig.Entrypoint}, c.ProcessConfig.Arguments...)
|
|
| 104 |
- ) |
|
| 105 |
- |
|
| 106 |
- if err := d.createContainerRoot(c.ID); err != nil {
|
|
| 134 |
+ if err := cont.Start(p); err != nil {
|
|
| 107 | 135 |
return execdriver.ExitStatus{ExitCode: -1}, err
|
| 108 | 136 |
} |
| 109 |
- defer d.cleanContainer(c.ID) |
|
| 110 | 137 |
|
| 111 |
- if err := d.writeContainerFile(container, c.ID); err != nil {
|
|
| 112 |
- return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 138 |
+ if startCallback != nil {
|
|
| 139 |
+ pid, err := p.Pid() |
|
| 140 |
+ if err != nil {
|
|
| 141 |
+ p.Signal(os.Kill) |
|
| 142 |
+ p.Wait() |
|
| 143 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 144 |
+ } |
|
| 145 |
+ startCallback(&c.ProcessConfig, pid) |
|
| 146 |
+ } |
|
| 147 |
+ |
|
| 148 |
+ oomKillNotification, err := cont.NotifyOOM() |
|
| 149 |
+ if err != nil {
|
|
| 150 |
+ oomKillNotification = nil |
|
| 151 |
+ log.Warnf("WARNING: Your kernel does not support OOM notifications: %s", err)
|
|
| 152 |
+ } |
|
| 153 |
+ waitF := p.Wait |
|
| 154 |
+ if nss := cont.Config().Namespaces; nss.Contains(configs.NEWPID) {
|
|
| 155 |
+ // we need such hack for tracking processes with inerited fds, |
|
| 156 |
+ // because cmd.Wait() waiting for all streams to be copied |
|
| 157 |
+ waitF = waitInPIDHost(p, cont) |
|
| 158 |
+ } |
|
| 159 |
+ ps, err := waitF() |
|
| 160 |
+ if err != nil {
|
|
| 161 |
+ if err, ok := err.(*exec.ExitError); !ok {
|
|
| 162 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 163 |
+ } else {
|
|
| 164 |
+ ps = err.ProcessState |
|
| 165 |
+ } |
|
| 113 | 166 |
} |
| 167 |
+ cont.Destroy() |
|
| 114 | 168 |
|
| 115 |
- execOutputChan := make(chan execOutput, 1) |
|
| 116 |
- waitForStart := make(chan struct{})
|
|
| 169 |
+ _, oomKill := <-oomKillNotification |
|
| 117 | 170 |
|
| 118 |
- go func() {
|
|
| 119 |
- exitCode, err := namespaces.Exec(container, c.ProcessConfig.Stdin, c.ProcessConfig.Stdout, c.ProcessConfig.Stderr, c.ProcessConfig.Console, dataPath, args, func(container *libcontainer.Config, console, dataPath, init string, child *os.File, args []string) *exec.Cmd {
|
|
| 120 |
- c.ProcessConfig.Path = d.initPath |
|
| 121 |
- c.ProcessConfig.Args = append([]string{
|
|
| 122 |
- DriverName, |
|
| 123 |
- "-console", console, |
|
| 124 |
- "-pipe", "3", |
|
| 125 |
- "-root", filepath.Join(d.root, c.ID), |
|
| 126 |
- "--", |
|
| 127 |
- }, args...) |
|
| 128 |
- |
|
| 129 |
- // set this to nil so that when we set the clone flags anything else is reset |
|
| 130 |
- c.ProcessConfig.SysProcAttr = &syscall.SysProcAttr{
|
|
| 131 |
- Cloneflags: uintptr(namespaces.GetNamespaceFlags(container.Namespaces)), |
|
| 132 |
- } |
|
| 133 |
- c.ProcessConfig.ExtraFiles = []*os.File{child}
|
|
| 171 |
+ return execdriver.ExitStatus{ExitCode: utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), OOMKilled: oomKill}, nil
|
|
| 172 |
+} |
|
| 134 | 173 |
|
| 135 |
- c.ProcessConfig.Env = container.Env |
|
| 136 |
- c.ProcessConfig.Dir = container.RootFs |
|
| 174 |
+func waitInPIDHost(p *libcontainer.Process, c libcontainer.Container) func() (*os.ProcessState, error) {
|
|
| 175 |
+ return func() (*os.ProcessState, error) {
|
|
| 176 |
+ pid, err := p.Pid() |
|
| 177 |
+ if err != nil {
|
|
| 178 |
+ return nil, err |
|
| 179 |
+ } |
|
| 137 | 180 |
|
| 138 |
- return &c.ProcessConfig.Cmd |
|
| 139 |
- }, func() {
|
|
| 140 |
- close(waitForStart) |
|
| 141 |
- if startCallback != nil {
|
|
| 142 |
- c.ContainerPid = c.ProcessConfig.Process.Pid |
|
| 143 |
- startCallback(&c.ProcessConfig, c.ContainerPid) |
|
| 181 |
+ process, err := os.FindProcess(pid) |
|
| 182 |
+ s, err := process.Wait() |
|
| 183 |
+ if err != nil {
|
|
| 184 |
+ if err, ok := err.(*exec.ExitError); !ok {
|
|
| 185 |
+ return s, err |
|
| 186 |
+ } else {
|
|
| 187 |
+ s = err.ProcessState |
|
| 144 | 188 |
} |
| 145 |
- }) |
|
| 146 |
- execOutputChan <- execOutput{exitCode, err}
|
|
| 147 |
- }() |
|
| 148 |
- |
|
| 149 |
- select {
|
|
| 150 |
- case execOutput := <-execOutputChan: |
|
| 151 |
- return execdriver.ExitStatus{ExitCode: execOutput.exitCode}, execOutput.err
|
|
| 152 |
- case <-waitForStart: |
|
| 153 |
- break |
|
| 154 |
- } |
|
| 189 |
+ } |
|
| 190 |
+ processes, err := c.Processes() |
|
| 191 |
+ if err != nil {
|
|
| 192 |
+ return s, err |
|
| 193 |
+ } |
|
| 155 | 194 |
|
| 156 |
- oomKill := false |
|
| 157 |
- state, err := libcontainer.GetState(filepath.Join(d.root, c.ID)) |
|
| 158 |
- if err == nil {
|
|
| 159 |
- oomKillNotification, err := libcontainer.NotifyOnOOM(state) |
|
| 160 |
- if err == nil {
|
|
| 161 |
- _, oomKill = <-oomKillNotification |
|
| 162 |
- } else {
|
|
| 163 |
- log.Warnf("WARNING: Your kernel does not support OOM notifications: %s", err)
|
|
| 195 |
+ for _, pid := range processes {
|
|
| 196 |
+ process, err := os.FindProcess(pid) |
|
| 197 |
+ if err != nil {
|
|
| 198 |
+ log.Errorf("Failed to kill process: %d", pid)
|
|
| 199 |
+ continue |
|
| 200 |
+ } |
|
| 201 |
+ process.Kill() |
|
| 164 | 202 |
} |
| 165 |
- } else {
|
|
| 166 |
- log.Warnf("Failed to get container state, oom notify will not work: %s", err)
|
|
| 167 |
- } |
|
| 168 |
- // wait for the container to exit. |
|
| 169 |
- execOutput := <-execOutputChan |
|
| 170 | 203 |
|
| 171 |
- return execdriver.ExitStatus{ExitCode: execOutput.exitCode, OOMKilled: oomKill}, execOutput.err
|
|
| 204 |
+ p.Wait() |
|
| 205 |
+ return s, err |
|
| 206 |
+ } |
|
| 172 | 207 |
} |
| 173 | 208 |
|
| 174 |
-func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
|
| 175 |
- if p.ProcessConfig.Process == nil {
|
|
| 176 |
- return errors.New("exec: not started")
|
|
| 209 |
+func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
|
| 210 |
+ active := d.activeContainers[c.ID] |
|
| 211 |
+ if active == nil {
|
|
| 212 |
+ return fmt.Errorf("active container for %s does not exist", c.ID)
|
|
| 177 | 213 |
} |
| 178 |
- return syscall.Kill(p.ProcessConfig.Process.Pid, syscall.Signal(sig)) |
|
| 214 |
+ state, err := active.State() |
|
| 215 |
+ if err != nil {
|
|
| 216 |
+ return err |
|
| 217 |
+ } |
|
| 218 |
+ return syscall.Kill(state.InitProcessPid, syscall.Signal(sig)) |
|
| 179 | 219 |
} |
| 180 | 220 |
|
| 181 | 221 |
func (d *driver) Pause(c *execdriver.Command) error {
|
| ... | ... |
@@ -183,11 +230,7 @@ func (d *driver) Pause(c *execdriver.Command) error {
|
| 183 | 183 |
if active == nil {
|
| 184 | 184 |
return fmt.Errorf("active container for %s does not exist", c.ID)
|
| 185 | 185 |
} |
| 186 |
- active.container.Cgroups.Freezer = "FROZEN" |
|
| 187 |
- if systemd.UseSystemd() {
|
|
| 188 |
- return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) |
|
| 189 |
- } |
|
| 190 |
- return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) |
|
| 186 |
+ return active.Pause() |
|
| 191 | 187 |
} |
| 192 | 188 |
|
| 193 | 189 |
func (d *driver) Unpause(c *execdriver.Command) error {
|
| ... | ... |
@@ -195,44 +238,31 @@ func (d *driver) Unpause(c *execdriver.Command) error {
|
| 195 | 195 |
if active == nil {
|
| 196 | 196 |
return fmt.Errorf("active container for %s does not exist", c.ID)
|
| 197 | 197 |
} |
| 198 |
- active.container.Cgroups.Freezer = "THAWED" |
|
| 199 |
- if systemd.UseSystemd() {
|
|
| 200 |
- return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) |
|
| 201 |
- } |
|
| 202 |
- return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer) |
|
| 198 |
+ return active.Resume() |
|
| 203 | 199 |
} |
| 204 | 200 |
|
| 205 |
-func (d *driver) Terminate(p *execdriver.Command) error {
|
|
| 201 |
+func (d *driver) Terminate(c *execdriver.Command) error {
|
|
| 206 | 202 |
// lets check the start time for the process |
| 207 |
- state, err := libcontainer.GetState(filepath.Join(d.root, p.ID)) |
|
| 203 |
+ active := d.activeContainers[c.ID] |
|
| 204 |
+ if active == nil {
|
|
| 205 |
+ return fmt.Errorf("active container for %s does not exist", c.ID)
|
|
| 206 |
+ } |
|
| 207 |
+ state, err := active.State() |
|
| 208 | 208 |
if err != nil {
|
| 209 |
- if !os.IsNotExist(err) {
|
|
| 210 |
- return err |
|
| 211 |
- } |
|
| 212 |
- // TODO: Remove this part for version 1.2.0 |
|
| 213 |
- // This is added only to ensure smooth upgrades from pre 1.1.0 to 1.1.0 |
|
| 214 |
- data, err := ioutil.ReadFile(filepath.Join(d.root, p.ID, "start")) |
|
| 215 |
- if err != nil {
|
|
| 216 |
- // if we don't have the data on disk then we can assume the process is gone |
|
| 217 |
- // because this is only removed after we know the process has stopped |
|
| 218 |
- if os.IsNotExist(err) {
|
|
| 219 |
- return nil |
|
| 220 |
- } |
|
| 221 |
- return err |
|
| 222 |
- } |
|
| 223 |
- state = &libcontainer.State{InitStartTime: string(data)}
|
|
| 209 |
+ return err |
|
| 224 | 210 |
} |
| 211 |
+ pid := state.InitProcessPid |
|
| 225 | 212 |
|
| 226 |
- currentStartTime, err := system.GetProcessStartTime(p.ProcessConfig.Process.Pid) |
|
| 213 |
+ currentStartTime, err := system.GetProcessStartTime(pid) |
|
| 227 | 214 |
if err != nil {
|
| 228 | 215 |
return err |
| 229 | 216 |
} |
| 230 | 217 |
|
| 231 |
- if state.InitStartTime == currentStartTime {
|
|
| 232 |
- err = syscall.Kill(p.ProcessConfig.Process.Pid, 9) |
|
| 233 |
- syscall.Wait4(p.ProcessConfig.Process.Pid, nil, 0, nil) |
|
| 218 |
+ if state.InitProcessStartTime == currentStartTime {
|
|
| 219 |
+ err = syscall.Kill(pid, 9) |
|
| 220 |
+ syscall.Wait4(pid, nil, 0, nil) |
|
| 234 | 221 |
} |
| 235 |
- d.cleanContainer(p.ID) |
|
| 222 |
+ d.cleanContainer(c.ID) |
|
| 236 | 223 |
|
| 237 | 224 |
return err |
| 238 | 225 |
|
| ... | ... |
@@ -257,15 +287,10 @@ func (d *driver) GetPidsForContainer(id string) ([]int, error) {
|
| 257 | 257 |
if active == nil {
|
| 258 | 258 |
return nil, fmt.Errorf("active container for %s does not exist", id)
|
| 259 | 259 |
} |
| 260 |
- c := active.container.Cgroups |
|
| 261 |
- |
|
| 262 |
- if systemd.UseSystemd() {
|
|
| 263 |
- return systemd.GetPids(c) |
|
| 264 |
- } |
|
| 265 |
- return fs.GetPids(c) |
|
| 260 |
+ return active.Processes() |
|
| 266 | 261 |
} |
| 267 | 262 |
|
| 268 |
-func (d *driver) writeContainerFile(container *libcontainer.Config, id string) error {
|
|
| 263 |
+func (d *driver) writeContainerFile(container *configs.Config, id string) error {
|
|
| 269 | 264 |
data, err := json.Marshal(container) |
| 270 | 265 |
if err != nil {
|
| 271 | 266 |
return err |
| ... | ... |
@@ -289,42 +314,61 @@ func (d *driver) Clean(id string) error {
|
| 289 | 289 |
} |
| 290 | 290 |
|
| 291 | 291 |
func (d *driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
| 292 |
- return execdriver.Stats(filepath.Join(d.root, id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory) |
|
| 292 |
+ c := d.activeContainers[id] |
|
| 293 |
+ now := time.Now() |
|
| 294 |
+ stats, err := c.Stats() |
|
| 295 |
+ if err != nil {
|
|
| 296 |
+ return nil, err |
|
| 297 |
+ } |
|
| 298 |
+ memoryLimit := c.Config().Cgroups.Memory |
|
| 299 |
+ // if the container does not have any memory limit specified set the |
|
| 300 |
+ // limit to the machines memory |
|
| 301 |
+ if memoryLimit == 0 {
|
|
| 302 |
+ memoryLimit = d.machineMemory |
|
| 303 |
+ } |
|
| 304 |
+ return &execdriver.ResourceStats{
|
|
| 305 |
+ Stats: stats, |
|
| 306 |
+ Read: now, |
|
| 307 |
+ MemoryLimit: memoryLimit, |
|
| 308 |
+ }, nil |
|
| 293 | 309 |
} |
| 294 | 310 |
|
| 295 |
-type TtyConsole struct {
|
|
| 296 |
- MasterPty *os.File |
|
| 311 |
+func getEnv(key string, env []string) string {
|
|
| 312 |
+ for _, pair := range env {
|
|
| 313 |
+ parts := strings.Split(pair, "=") |
|
| 314 |
+ if parts[0] == key {
|
|
| 315 |
+ return parts[1] |
|
| 316 |
+ } |
|
| 317 |
+ } |
|
| 318 |
+ return "" |
|
| 297 | 319 |
} |
| 298 | 320 |
|
| 299 |
-func NewTtyConsole(processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes) (*TtyConsole, error) {
|
|
| 300 |
- ptyMaster, console, err := consolepkg.CreateMasterAndConsole() |
|
| 301 |
- if err != nil {
|
|
| 302 |
- return nil, err |
|
| 303 |
- } |
|
| 321 |
+type TtyConsole struct {
|
|
| 322 |
+ console libcontainer.Console |
|
| 323 |
+} |
|
| 304 | 324 |
|
| 325 |
+func NewTtyConsole(console libcontainer.Console, pipes *execdriver.Pipes, rootuid int) (*TtyConsole, error) {
|
|
| 305 | 326 |
tty := &TtyConsole{
|
| 306 |
- MasterPty: ptyMaster, |
|
| 327 |
+ console: console, |
|
| 307 | 328 |
} |
| 308 | 329 |
|
| 309 |
- if err := tty.AttachPipes(&processConfig.Cmd, pipes); err != nil {
|
|
| 330 |
+ if err := tty.AttachPipes(pipes); err != nil {
|
|
| 310 | 331 |
tty.Close() |
| 311 | 332 |
return nil, err |
| 312 | 333 |
} |
| 313 | 334 |
|
| 314 |
- processConfig.Console = console |
|
| 315 |
- |
|
| 316 | 335 |
return tty, nil |
| 317 | 336 |
} |
| 318 | 337 |
|
| 319 |
-func (t *TtyConsole) Master() *os.File {
|
|
| 320 |
- return t.MasterPty |
|
| 338 |
+func (t *TtyConsole) Master() libcontainer.Console {
|
|
| 339 |
+ return t.console |
|
| 321 | 340 |
} |
| 322 | 341 |
|
| 323 | 342 |
func (t *TtyConsole) Resize(h, w int) error {
|
| 324 |
- return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
|
| 343 |
+ return term.SetWinsize(t.console.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
|
|
| 325 | 344 |
} |
| 326 | 345 |
|
| 327 |
-func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
|
|
| 346 |
+func (t *TtyConsole) AttachPipes(pipes *execdriver.Pipes) error {
|
|
| 328 | 347 |
go func() {
|
| 329 | 348 |
if wb, ok := pipes.Stdout.(interface {
|
| 330 | 349 |
CloseWriters() error |
| ... | ... |
@@ -332,12 +376,12 @@ func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) err |
| 332 | 332 |
defer wb.CloseWriters() |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
- io.Copy(pipes.Stdout, t.MasterPty) |
|
| 335 |
+ io.Copy(pipes.Stdout, t.console) |
|
| 336 | 336 |
}() |
| 337 | 337 |
|
| 338 | 338 |
if pipes.Stdin != nil {
|
| 339 | 339 |
go func() {
|
| 340 |
- io.Copy(t.MasterPty, pipes.Stdin) |
|
| 340 |
+ io.Copy(t.console, pipes.Stdin) |
|
| 341 | 341 |
|
| 342 | 342 |
pipes.Stdin.Close() |
| 343 | 343 |
}() |
| ... | ... |
@@ -347,5 +391,5 @@ func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) err |
| 347 | 347 |
} |
| 348 | 348 |
|
| 349 | 349 |
func (t *TtyConsole) Close() error {
|
| 350 |
- return t.MasterPty.Close() |
|
| 350 |
+ return t.console.Close() |
|
| 351 | 351 |
} |
| ... | ... |
@@ -4,67 +4,77 @@ package native |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
- "log" |
|
| 8 | 7 |
"os" |
| 9 | 8 |
"os/exec" |
| 10 |
- "path/filepath" |
|
| 11 |
- "runtime" |
|
| 9 |
+ "syscall" |
|
| 12 | 10 |
|
| 13 | 11 |
"github.com/docker/docker/daemon/execdriver" |
| 14 |
- "github.com/docker/docker/pkg/reexec" |
|
| 15 | 12 |
"github.com/docker/libcontainer" |
| 16 |
- "github.com/docker/libcontainer/namespaces" |
|
| 13 |
+ _ "github.com/docker/libcontainer/nsenter" |
|
| 14 |
+ "github.com/docker/libcontainer/utils" |
|
| 17 | 15 |
) |
| 18 | 16 |
|
| 19 |
-const execCommandName = "nsenter-exec" |
|
| 20 |
- |
|
| 21 |
-func init() {
|
|
| 22 |
- reexec.Register(execCommandName, nsenterExec) |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-func nsenterExec() {
|
|
| 26 |
- runtime.LockOSThread() |
|
| 27 |
- |
|
| 28 |
- // User args are passed after '--' in the command line. |
|
| 29 |
- userArgs := findUserArgs() |
|
| 30 |
- |
|
| 31 |
- config, err := loadConfigFromFd() |
|
| 32 |
- if err != nil {
|
|
| 33 |
- log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err)
|
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 |
- if err := namespaces.FinalizeSetns(config, userArgs); err != nil {
|
|
| 37 |
- log.Fatalf("docker-exec: failed to exec: %s", err)
|
|
| 38 |
- } |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 | 17 |
// TODO(vishh): Add support for running in priviledged mode and running as a different user. |
| 42 | 18 |
func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
| 43 | 19 |
active := d.activeContainers[c.ID] |
| 44 | 20 |
if active == nil {
|
| 45 | 21 |
return -1, fmt.Errorf("No active container exists with ID %s", c.ID)
|
| 46 | 22 |
} |
| 47 |
- state, err := libcontainer.GetState(filepath.Join(d.root, c.ID)) |
|
| 48 |
- if err != nil {
|
|
| 49 |
- return -1, fmt.Errorf("State unavailable for container with ID %s. The container may have been cleaned up already. Error: %s", c.ID, err)
|
|
| 50 |
- } |
|
| 51 | 23 |
|
| 52 | 24 |
var term execdriver.Terminal |
| 25 |
+ var err error |
|
| 26 |
+ |
|
| 27 |
+ p := &libcontainer.Process{
|
|
| 28 |
+ Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...),
|
|
| 29 |
+ Env: c.ProcessConfig.Env, |
|
| 30 |
+ Cwd: c.WorkingDir, |
|
| 31 |
+ User: c.ProcessConfig.User, |
|
| 32 |
+ } |
|
| 53 | 33 |
|
| 54 | 34 |
if processConfig.Tty {
|
| 55 |
- term, err = NewTtyConsole(processConfig, pipes) |
|
| 35 |
+ config := active.Config() |
|
| 36 |
+ rootuid, err := config.HostUID() |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ return -1, err |
|
| 39 |
+ } |
|
| 40 |
+ cons, err := p.NewConsole(rootuid) |
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ return -1, err |
|
| 43 |
+ } |
|
| 44 |
+ term, err = NewTtyConsole(cons, pipes, rootuid) |
|
| 56 | 45 |
} else {
|
| 57 |
- term, err = execdriver.NewStdConsole(processConfig, pipes) |
|
| 46 |
+ p.Stdout = pipes.Stdout |
|
| 47 |
+ p.Stderr = pipes.Stderr |
|
| 48 |
+ p.Stdin = pipes.Stdin |
|
| 49 |
+ term = &execdriver.StdConsole{}
|
|
| 50 |
+ } |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ return -1, err |
|
| 58 | 53 |
} |
| 59 | 54 |
|
| 60 | 55 |
processConfig.Terminal = term |
| 61 | 56 |
|
| 62 |
- args := append([]string{processConfig.Entrypoint}, processConfig.Arguments...)
|
|
| 57 |
+ if err := active.Start(p); err != nil {
|
|
| 58 |
+ return -1, err |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ if startCallback != nil {
|
|
| 62 |
+ pid, err := p.Pid() |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ p.Signal(os.Kill) |
|
| 65 |
+ p.Wait() |
|
| 66 |
+ return -1, err |
|
| 67 |
+ } |
|
| 68 |
+ startCallback(&c.ProcessConfig, pid) |
|
| 69 |
+ } |
|
| 63 | 70 |
|
| 64 |
- return namespaces.ExecIn(active.container, state, args, os.Args[0], "exec", processConfig.Stdin, processConfig.Stdout, processConfig.Stderr, processConfig.Console, |
|
| 65 |
- func(cmd *exec.Cmd) {
|
|
| 66 |
- if startCallback != nil {
|
|
| 67 |
- startCallback(&c.ProcessConfig, cmd.Process.Pid) |
|
| 68 |
- } |
|
| 69 |
- }) |
|
| 71 |
+ ps, err := p.Wait() |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ exitErr, ok := err.(*exec.ExitError) |
|
| 74 |
+ if !ok {
|
|
| 75 |
+ return -1, err |
|
| 76 |
+ } |
|
| 77 |
+ ps = exitErr.ProcessState |
|
| 78 |
+ } |
|
| 79 |
+ return utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), nil |
|
| 70 | 80 |
} |
| ... | ... |
@@ -2,13 +2,6 @@ |
| 2 | 2 |
|
| 3 | 3 |
package native |
| 4 | 4 |
|
| 5 |
-import ( |
|
| 6 |
- "os" |
|
| 7 |
- "path/filepath" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/libcontainer" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 | 5 |
type info struct {
|
| 13 | 6 |
ID string |
| 14 | 7 |
driver *driver |
| ... | ... |
@@ -18,13 +11,6 @@ type info struct {
|
| 18 | 18 |
// pid file for a container. If the file exists then the |
| 19 | 19 |
// container is currently running |
| 20 | 20 |
func (i *info) IsRunning() bool {
|
| 21 |
- if _, err := libcontainer.GetState(filepath.Join(i.driver.root, i.ID)); err == nil {
|
|
| 22 |
- return true |
|
| 23 |
- } |
|
| 24 |
- // TODO: Remove this part for version 1.2.0 |
|
| 25 |
- // This is added only to ensure smooth upgrades from pre 1.1.0 to 1.1.0 |
|
| 26 |
- if _, err := os.Stat(filepath.Join(i.driver.root, i.ID, "pid")); err == nil {
|
|
| 27 |
- return true |
|
| 28 |
- } |
|
| 29 |
- return false |
|
| 21 |
+ _, ok := i.driver.activeContainers[i.ID] |
|
| 22 |
+ return ok |
|
| 30 | 23 |
} |
| ... | ... |
@@ -3,55 +3,40 @@ |
| 3 | 3 |
package native |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "encoding/json" |
|
| 7 |
- "flag" |
|
| 8 | 6 |
"fmt" |
| 9 | 7 |
"os" |
| 10 |
- "path/filepath" |
|
| 11 | 8 |
"runtime" |
| 12 | 9 |
|
| 13 | 10 |
"github.com/docker/docker/pkg/reexec" |
| 14 | 11 |
"github.com/docker/libcontainer" |
| 15 |
- "github.com/docker/libcontainer/namespaces" |
|
| 16 | 12 |
) |
| 17 | 13 |
|
| 18 | 14 |
func init() {
|
| 19 | 15 |
reexec.Register(DriverName, initializer) |
| 20 | 16 |
} |
| 21 | 17 |
|
| 22 |
-func initializer() {
|
|
| 23 |
- runtime.LockOSThread() |
|
| 24 |
- |
|
| 25 |
- var ( |
|
| 26 |
- pipe = flag.Int("pipe", 0, "sync pipe fd")
|
|
| 27 |
- console = flag.String("console", "", "console (pty slave) path")
|
|
| 28 |
- root = flag.String("root", ".", "root path for configuration files")
|
|
| 29 |
- ) |
|
| 30 |
- |
|
| 31 |
- flag.Parse() |
|
| 32 |
- |
|
| 33 |
- var container *libcontainer.Config |
|
| 34 |
- f, err := os.Open(filepath.Join(*root, "container.json")) |
|
| 35 |
- if err != nil {
|
|
| 36 |
- writeError(err) |
|
| 18 |
+func fatal(err error) {
|
|
| 19 |
+ if lerr, ok := err.(libcontainer.Error); ok {
|
|
| 20 |
+ lerr.Detail(os.Stderr) |
|
| 21 |
+ os.Exit(1) |
|
| 37 | 22 |
} |
| 38 | 23 |
|
| 39 |
- if err := json.NewDecoder(f).Decode(&container); err != nil {
|
|
| 40 |
- f.Close() |
|
| 41 |
- writeError(err) |
|
| 42 |
- } |
|
| 43 |
- f.Close() |
|
| 24 |
+ fmt.Fprintln(os.Stderr, err) |
|
| 25 |
+ os.Exit(1) |
|
| 26 |
+} |
|
| 44 | 27 |
|
| 45 |
- rootfs, err := os.Getwd() |
|
| 28 |
+func initializer() {
|
|
| 29 |
+ runtime.GOMAXPROCS(1) |
|
| 30 |
+ runtime.LockOSThread() |
|
| 31 |
+ factory, err := libcontainer.New("")
|
|
| 46 | 32 |
if err != nil {
|
| 47 |
- writeError(err) |
|
| 33 |
+ fatal(err) |
|
| 48 | 34 |
} |
| 49 |
- |
|
| 50 |
- if err := namespaces.Init(container, rootfs, *console, os.NewFile(uintptr(*pipe), "child"), flag.Args()); err != nil {
|
|
| 51 |
- writeError(err) |
|
| 35 |
+ if err := factory.StartInitialization(3); err != nil {
|
|
| 36 |
+ fatal(err) |
|
| 52 | 37 |
} |
| 53 | 38 |
|
| 54 |
- panic("Unreachable")
|
|
| 39 |
+ panic("unreachable")
|
|
| 55 | 40 |
} |
| 56 | 41 |
|
| 57 | 42 |
func writeError(err error) {
|
| ... | ... |
@@ -1,14 +1,17 @@ |
| 1 | 1 |
package template |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/docker/libcontainer" |
|
| 4 |
+ "syscall" |
|
| 5 |
+ |
|
| 5 | 6 |
"github.com/docker/libcontainer/apparmor" |
| 6 |
- "github.com/docker/libcontainer/cgroups" |
|
| 7 |
+ "github.com/docker/libcontainer/configs" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 10 |
+const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV |
|
| 11 |
+ |
|
| 9 | 12 |
// New returns the docker default configuration for libcontainer |
| 10 |
-func New() *libcontainer.Config {
|
|
| 11 |
- container := &libcontainer.Config{
|
|
| 13 |
+func New() *configs.Config {
|
|
| 14 |
+ container := &configs.Config{
|
|
| 12 | 15 |
Capabilities: []string{
|
| 13 | 16 |
"CHOWN", |
| 14 | 17 |
"DAC_OVERRIDE", |
| ... | ... |
@@ -25,18 +28,51 @@ func New() *libcontainer.Config {
|
| 25 | 25 |
"KILL", |
| 26 | 26 |
"AUDIT_WRITE", |
| 27 | 27 |
}, |
| 28 |
- Namespaces: libcontainer.Namespaces([]libcontainer.Namespace{
|
|
| 28 |
+ Namespaces: configs.Namespaces([]configs.Namespace{
|
|
| 29 | 29 |
{Type: "NEWNS"},
|
| 30 | 30 |
{Type: "NEWUTS"},
|
| 31 | 31 |
{Type: "NEWIPC"},
|
| 32 | 32 |
{Type: "NEWPID"},
|
| 33 | 33 |
{Type: "NEWNET"},
|
| 34 | 34 |
}), |
| 35 |
- Cgroups: &cgroups.Cgroup{
|
|
| 35 |
+ Cgroups: &configs.Cgroup{
|
|
| 36 | 36 |
Parent: "docker", |
| 37 | 37 |
AllowAllDevices: false, |
| 38 | 38 |
}, |
| 39 |
- MountConfig: &libcontainer.MountConfig{},
|
|
| 39 |
+ Mounts: []*configs.Mount{
|
|
| 40 |
+ {
|
|
| 41 |
+ Device: "tmpfs", |
|
| 42 |
+ Source: "shm", |
|
| 43 |
+ Destination: "/dev/shm", |
|
| 44 |
+ Data: "mode=1777,size=65536k", |
|
| 45 |
+ Flags: defaultMountFlags, |
|
| 46 |
+ }, |
|
| 47 |
+ {
|
|
| 48 |
+ Source: "mqueue", |
|
| 49 |
+ Destination: "/dev/mqueue", |
|
| 50 |
+ Device: "mqueue", |
|
| 51 |
+ Flags: defaultMountFlags, |
|
| 52 |
+ }, |
|
| 53 |
+ {
|
|
| 54 |
+ Source: "sysfs", |
|
| 55 |
+ Destination: "/sys", |
|
| 56 |
+ Device: "sysfs", |
|
| 57 |
+ Flags: defaultMountFlags | syscall.MS_RDONLY, |
|
| 58 |
+ }, |
|
| 59 |
+ }, |
|
| 60 |
+ MaskPaths: []string{
|
|
| 61 |
+ "/proc/kcore", |
|
| 62 |
+ }, |
|
| 63 |
+ ReadonlyPaths: []string{
|
|
| 64 |
+ "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", |
|
| 65 |
+ }, |
|
| 66 |
+ Rlimits: []configs.Rlimit{
|
|
| 67 |
+ {
|
|
| 68 |
+ Type: syscall.RLIMIT_NOFILE, |
|
| 69 |
+ Hard: 1024, |
|
| 70 |
+ Soft: 1024, |
|
| 71 |
+ }, |
|
| 72 |
+ }, |
|
| 40 | 73 |
} |
| 41 | 74 |
|
| 42 | 75 |
if apparmor.IsEnabled() {
|
| ... | ... |
@@ -2,28 +2,21 @@ |
| 2 | 2 |
|
| 3 | 3 |
package native |
| 4 | 4 |
|
| 5 |
-import ( |
|
| 6 |
- "encoding/json" |
|
| 7 |
- "os" |
|
| 5 |
+//func findUserArgs() []string {
|
|
| 6 |
+//for i, a := range os.Args {
|
|
| 7 |
+//if a == "--" {
|
|
| 8 |
+//return os.Args[i+1:] |
|
| 9 |
+//} |
|
| 10 |
+//} |
|
| 11 |
+//return []string{}
|
|
| 12 |
+//} |
|
| 8 | 13 |
|
| 9 |
- "github.com/docker/libcontainer" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func findUserArgs() []string {
|
|
| 13 |
- for i, a := range os.Args {
|
|
| 14 |
- if a == "--" {
|
|
| 15 |
- return os.Args[i+1:] |
|
| 16 |
- } |
|
| 17 |
- } |
|
| 18 |
- return []string{}
|
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-// loadConfigFromFd loads a container's config from the sync pipe that is provided by |
|
| 22 |
-// fd 3 when running a process |
|
| 23 |
-func loadConfigFromFd() (*libcontainer.Config, error) {
|
|
| 24 |
- var config *libcontainer.Config |
|
| 25 |
- if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil {
|
|
| 26 |
- return nil, err |
|
| 27 |
- } |
|
| 28 |
- return config, nil |
|
| 29 |
-} |
|
| 14 |
+//// loadConfigFromFd loads a container's config from the sync pipe that is provided by |
|
| 15 |
+//// fd 3 when running a process |
|
| 16 |
+//func loadConfigFromFd() (*configs.Config, error) {
|
|
| 17 |
+//var config *libcontainer.Config |
|
| 18 |
+//if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil {
|
|
| 19 |
+//return nil, err |
|
| 20 |
+//} |
|
| 21 |
+//return config, nil |
|
| 22 |
+//} |
| ... | ... |
@@ -5,13 +5,83 @@ import ( |
| 5 | 5 |
"strings" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/docker/docker/utils" |
| 8 |
- "github.com/docker/libcontainer/security/capabilities" |
|
| 8 |
+ "github.com/syndtr/gocapability/capability" |
|
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+var capabilityList = Capabilities{
|
|
| 12 |
+ {Key: "SETPCAP", Value: capability.CAP_SETPCAP},
|
|
| 13 |
+ {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
|
|
| 14 |
+ {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
|
|
| 15 |
+ {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
|
|
| 16 |
+ {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
|
|
| 17 |
+ {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
|
|
| 18 |
+ {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
|
|
| 19 |
+ {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
|
|
| 20 |
+ {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
|
|
| 21 |
+ {Key: "MKNOD", Value: capability.CAP_MKNOD},
|
|
| 22 |
+ {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
|
|
| 23 |
+ {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
|
|
| 24 |
+ {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
|
|
| 25 |
+ {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
|
|
| 26 |
+ {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
|
|
| 27 |
+ {Key: "SYSLOG", Value: capability.CAP_SYSLOG},
|
|
| 28 |
+ {Key: "CHOWN", Value: capability.CAP_CHOWN},
|
|
| 29 |
+ {Key: "NET_RAW", Value: capability.CAP_NET_RAW},
|
|
| 30 |
+ {Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
|
|
| 31 |
+ {Key: "FOWNER", Value: capability.CAP_FOWNER},
|
|
| 32 |
+ {Key: "DAC_READ_SEARCH", Value: capability.CAP_DAC_READ_SEARCH},
|
|
| 33 |
+ {Key: "FSETID", Value: capability.CAP_FSETID},
|
|
| 34 |
+ {Key: "KILL", Value: capability.CAP_KILL},
|
|
| 35 |
+ {Key: "SETGID", Value: capability.CAP_SETGID},
|
|
| 36 |
+ {Key: "SETUID", Value: capability.CAP_SETUID},
|
|
| 37 |
+ {Key: "LINUX_IMMUTABLE", Value: capability.CAP_LINUX_IMMUTABLE},
|
|
| 38 |
+ {Key: "NET_BIND_SERVICE", Value: capability.CAP_NET_BIND_SERVICE},
|
|
| 39 |
+ {Key: "NET_BROADCAST", Value: capability.CAP_NET_BROADCAST},
|
|
| 40 |
+ {Key: "IPC_LOCK", Value: capability.CAP_IPC_LOCK},
|
|
| 41 |
+ {Key: "IPC_OWNER", Value: capability.CAP_IPC_OWNER},
|
|
| 42 |
+ {Key: "SYS_CHROOT", Value: capability.CAP_SYS_CHROOT},
|
|
| 43 |
+ {Key: "SYS_PTRACE", Value: capability.CAP_SYS_PTRACE},
|
|
| 44 |
+ {Key: "SYS_BOOT", Value: capability.CAP_SYS_BOOT},
|
|
| 45 |
+ {Key: "LEASE", Value: capability.CAP_LEASE},
|
|
| 46 |
+ {Key: "SETFCAP", Value: capability.CAP_SETFCAP},
|
|
| 47 |
+ {Key: "WAKE_ALARM", Value: capability.CAP_WAKE_ALARM},
|
|
| 48 |
+ {Key: "BLOCK_SUSPEND", Value: capability.CAP_BLOCK_SUSPEND},
|
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+type ( |
|
| 52 |
+ CapabilityMapping struct {
|
|
| 53 |
+ Key string `json:"key,omitempty"` |
|
| 54 |
+ Value capability.Cap `json:"value,omitempty"` |
|
| 55 |
+ } |
|
| 56 |
+ Capabilities []*CapabilityMapping |
|
| 57 |
+) |
|
| 58 |
+ |
|
| 59 |
+func (c *CapabilityMapping) String() string {
|
|
| 60 |
+ return c.Key |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func GetCapability(key string) *CapabilityMapping {
|
|
| 64 |
+ for _, capp := range capabilityList {
|
|
| 65 |
+ if capp.Key == key {
|
|
| 66 |
+ cpy := *capp |
|
| 67 |
+ return &cpy |
|
| 68 |
+ } |
|
| 69 |
+ } |
|
| 70 |
+ return nil |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func GetAllCapabilities() []string {
|
|
| 74 |
+ output := make([]string, len(capabilityList)) |
|
| 75 |
+ for i, capability := range capabilityList {
|
|
| 76 |
+ output[i] = capability.String() |
|
| 77 |
+ } |
|
| 78 |
+ return output |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 11 | 81 |
func TweakCapabilities(basics, adds, drops []string) ([]string, error) {
|
| 12 | 82 |
var ( |
| 13 | 83 |
newCaps []string |
| 14 |
- allCaps = capabilities.GetAllCapabilities() |
|
| 84 |
+ allCaps = GetAllCapabilities() |
|
| 15 | 85 |
) |
| 16 | 86 |
|
| 17 | 87 |
// look for invalid cap in the drop list |
| ... | ... |
@@ -26,7 +96,7 @@ func TweakCapabilities(basics, adds, drops []string) ([]string, error) {
|
| 26 | 26 |
|
| 27 | 27 |
// handle --cap-add=all |
| 28 | 28 |
if utils.StringsContainsNoCase(adds, "all") {
|
| 29 |
- basics = capabilities.GetAllCapabilities() |
|
| 29 |
+ basics = allCaps |
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
if !utils.StringsContainsNoCase(drops, "all") {
|
| ... | ... |
@@ -18,7 +18,7 @@ func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status {
|
| 18 | 18 |
enc := json.NewEncoder(job.Stdout) |
| 19 | 19 |
for v := range updates {
|
| 20 | 20 |
update := v.(*execdriver.ResourceStats) |
| 21 |
- ss := convertToAPITypes(update.ContainerStats) |
|
| 21 |
+ ss := convertToAPITypes(update.Stats) |
|
| 22 | 22 |
ss.MemoryStats.Limit = uint64(update.MemoryLimit) |
| 23 | 23 |
ss.Read = update.Read |
| 24 | 24 |
ss.CpuStats.SystemUsage = update.SystemUsage |
| ... | ... |
@@ -31,20 +31,21 @@ func (daemon *Daemon) ContainerStats(job *engine.Job) engine.Status {
|
| 31 | 31 |
return engine.StatusOK |
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 |
-// convertToAPITypes converts the libcontainer.ContainerStats to the api specific |
|
| 34 |
+// convertToAPITypes converts the libcontainer.Stats to the api specific |
|
| 35 | 35 |
// structs. This is done to preserve API compatibility and versioning. |
| 36 |
-func convertToAPITypes(ls *libcontainer.ContainerStats) *types.Stats {
|
|
| 36 |
+func convertToAPITypes(ls *libcontainer.Stats) *types.Stats {
|
|
| 37 | 37 |
s := &types.Stats{}
|
| 38 |
- if ls.NetworkStats != nil {
|
|
| 39 |
- s.Network = types.Network{
|
|
| 40 |
- RxBytes: ls.NetworkStats.RxBytes, |
|
| 41 |
- RxPackets: ls.NetworkStats.RxPackets, |
|
| 42 |
- RxErrors: ls.NetworkStats.RxErrors, |
|
| 43 |
- RxDropped: ls.NetworkStats.RxDropped, |
|
| 44 |
- TxBytes: ls.NetworkStats.TxBytes, |
|
| 45 |
- TxPackets: ls.NetworkStats.TxPackets, |
|
| 46 |
- TxErrors: ls.NetworkStats.TxErrors, |
|
| 47 |
- TxDropped: ls.NetworkStats.TxDropped, |
|
| 38 |
+ if ls.Interfaces != nil {
|
|
| 39 |
+ s.Network = types.Network{}
|
|
| 40 |
+ for _, iface := range ls.Interfaces {
|
|
| 41 |
+ s.Network.RxBytes += iface.RxBytes |
|
| 42 |
+ s.Network.RxPackets += iface.RxPackets |
|
| 43 |
+ s.Network.RxErrors += iface.RxErrors |
|
| 44 |
+ s.Network.RxDropped += iface.RxDropped |
|
| 45 |
+ s.Network.TxBytes += iface.TxBytes |
|
| 46 |
+ s.Network.TxPackets += iface.TxPackets |
|
| 47 |
+ s.Network.TxErrors += iface.TxErrors |
|
| 48 |
+ s.Network.TxDropped += iface.TxDropped |
|
| 48 | 49 |
} |
| 49 | 50 |
} |
| 50 | 51 |
cs := ls.CgroupStats |
| ... | ... |
@@ -60,7 +60,7 @@ func TestExecInteractiveStdinClose(t *testing.T) {
|
| 60 | 60 |
|
| 61 | 61 |
out, err := cmd.CombinedOutput() |
| 62 | 62 |
if err != nil {
|
| 63 |
- t.Fatal(err, out) |
|
| 63 |
+ t.Fatal(err, string(out)) |
|
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 | 66 |
if string(out) == "" {
|
| ... | ... |
@@ -538,7 +538,6 @@ func TestRunExecDir(t *testing.T) {
|
| 538 | 538 |
id := strings.TrimSpace(out) |
| 539 | 539 |
execDir := filepath.Join(execDriverPath, id) |
| 540 | 540 |
stateFile := filepath.Join(execDir, "state.json") |
| 541 |
- contFile := filepath.Join(execDir, "container.json") |
|
| 542 | 541 |
|
| 543 | 542 |
{
|
| 544 | 543 |
fi, err := os.Stat(execDir) |
| ... | ... |
@@ -552,10 +551,6 @@ func TestRunExecDir(t *testing.T) {
|
| 552 | 552 |
if err != nil {
|
| 553 | 553 |
t.Fatal(err) |
| 554 | 554 |
} |
| 555 |
- fi, err = os.Stat(contFile) |
|
| 556 |
- if err != nil {
|
|
| 557 |
- t.Fatal(err) |
|
| 558 |
- } |
|
| 559 | 555 |
} |
| 560 | 556 |
|
| 561 | 557 |
stopCmd := exec.Command(dockerBinary, "stop", id) |
| ... | ... |
@@ -564,23 +559,12 @@ func TestRunExecDir(t *testing.T) {
|
| 564 | 564 |
t.Fatal(err, out) |
| 565 | 565 |
} |
| 566 | 566 |
{
|
| 567 |
- fi, err := os.Stat(execDir) |
|
| 568 |
- if err != nil {
|
|
| 569 |
- t.Fatal(err) |
|
| 570 |
- } |
|
| 571 |
- if !fi.IsDir() {
|
|
| 572 |
- t.Fatalf("%q must be a directory", execDir)
|
|
| 573 |
- } |
|
| 574 |
- fi, err = os.Stat(stateFile) |
|
| 567 |
+ _, err := os.Stat(execDir) |
|
| 575 | 568 |
if err == nil {
|
| 576 |
- t.Fatalf("Statefile %q is exists for stopped container!", stateFile)
|
|
| 577 |
- } |
|
| 578 |
- if !os.IsNotExist(err) {
|
|
| 579 |
- t.Fatalf("Error should be about non-existing, got %s", err)
|
|
| 569 |
+ t.Fatal(err) |
|
| 580 | 570 |
} |
| 581 |
- fi, err = os.Stat(contFile) |
|
| 582 | 571 |
if err == nil {
|
| 583 |
- t.Fatalf("Container file %q is exists for stopped container!", contFile)
|
|
| 572 |
+ t.Fatalf("Exec directory %q exists for removed container!", execDir)
|
|
| 584 | 573 |
} |
| 585 | 574 |
if !os.IsNotExist(err) {
|
| 586 | 575 |
t.Fatalf("Error should be about non-existing, got %s", err)
|
| ... | ... |
@@ -603,10 +587,6 @@ func TestRunExecDir(t *testing.T) {
|
| 603 | 603 |
if err != nil {
|
| 604 | 604 |
t.Fatal(err) |
| 605 | 605 |
} |
| 606 |
- fi, err = os.Stat(contFile) |
|
| 607 |
- if err != nil {
|
|
| 608 |
- t.Fatal(err) |
|
| 609 |
- } |
|
| 610 | 606 |
} |
| 611 | 607 |
rmCmd := exec.Command(dockerBinary, "rm", "-f", id) |
| 612 | 608 |
out, _, err = runCommandWithOutput(rmCmd) |