Handle external mounts outside of lxc
This commit is contained in:
parent
f768c6adb7
commit
45d7dcfea2
4 changed files with 209 additions and 33 deletions
46
container.go
46
container.go
|
@ -48,7 +48,6 @@ type Container struct {
|
||||||
network *NetworkInterface
|
network *NetworkInterface
|
||||||
NetworkSettings *NetworkSettings
|
NetworkSettings *NetworkSettings
|
||||||
|
|
||||||
SysInitPath string
|
|
||||||
ResolvConfPath string
|
ResolvConfPath string
|
||||||
HostnamePath string
|
HostnamePath string
|
||||||
HostsPath string
|
HostsPath string
|
||||||
|
@ -297,7 +296,11 @@ func (container *Container) generateEnvConfig(env []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(container.EnvConfigPath(), data, 0600)
|
p, err := container.EnvConfigPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ioutil.WriteFile(p, data, 0600)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,6 +684,17 @@ func (container *Container) Start() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mounts, err := runtime.getMounts(container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range mounts {
|
||||||
|
if err := m.Mount(container.RootfsPath()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
container.cmd = exec.Command(params[0], params[1:]...)
|
container.cmd = exec.Command(params[0], params[1:]...)
|
||||||
|
|
||||||
// Setup logging of stdout and stderr to disk
|
// Setup logging of stdout and stderr to disk
|
||||||
|
@ -1358,6 +1372,18 @@ func (container *Container) GetImage() (*Image, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) Unmount() error {
|
func (container *Container) Unmount() error {
|
||||||
|
mounts, err := container.runtime.getMounts(container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, m := range mounts {
|
||||||
|
if lastError := m.Unmount(container.RootfsPath()); lastError != nil {
|
||||||
|
err = lastError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return container.runtime.Unmount(container)
|
return container.runtime.Unmount(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,8 +1403,20 @@ func (container *Container) jsonPath() string {
|
||||||
return path.Join(container.root, "config.json")
|
return path.Join(container.root, "config.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) EnvConfigPath() string {
|
func (container *Container) EnvConfigPath() (string, error) {
|
||||||
return path.Join(container.root, "config.env")
|
p := path.Join(container.root, "config.env")
|
||||||
|
if _, err := os.Stat(p); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
f, err := os.Create(p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
} else {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) lxcConfigPath() string {
|
func (container *Container) lxcConfigPath() string {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InitFunc func(root string) (Driver, error)
|
type InitFunc func(root string) (Driver, error)
|
||||||
|
@ -31,6 +33,13 @@ type Differ interface {
|
||||||
DiffSize(id string) (bytes int64, err error)
|
DiffSize(id string) (bytes int64, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mount struct {
|
||||||
|
Device string
|
||||||
|
Target string
|
||||||
|
Type string
|
||||||
|
Options string
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultDriver string
|
DefaultDriver string
|
||||||
// All registred drivers
|
// All registred drivers
|
||||||
|
@ -88,3 +97,91 @@ func New(root string) (driver Driver, err error) {
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Mount) Mount(root string) error {
|
||||||
|
var (
|
||||||
|
flag int
|
||||||
|
data []string
|
||||||
|
target = path.Join(root, m.Target)
|
||||||
|
)
|
||||||
|
|
||||||
|
if mounted, err := Mounted(target); err != nil || mounted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := map[string]struct {
|
||||||
|
clear bool
|
||||||
|
flag int
|
||||||
|
}{
|
||||||
|
"defaults": {false, 0},
|
||||||
|
"ro": {false, syscall.MS_RDONLY},
|
||||||
|
"rw": {true, syscall.MS_RDONLY},
|
||||||
|
"suid": {true, syscall.MS_NOSUID},
|
||||||
|
"nosuid": {false, syscall.MS_NOSUID},
|
||||||
|
"dev": {true, syscall.MS_NODEV},
|
||||||
|
"nodev": {false, syscall.MS_NODEV},
|
||||||
|
"exec": {true, syscall.MS_NOEXEC},
|
||||||
|
"noexec": {false, syscall.MS_NOEXEC},
|
||||||
|
"sync": {false, syscall.MS_SYNCHRONOUS},
|
||||||
|
"async": {true, syscall.MS_SYNCHRONOUS},
|
||||||
|
"dirsync": {false, syscall.MS_DIRSYNC},
|
||||||
|
"remount": {false, syscall.MS_REMOUNT},
|
||||||
|
"mand": {false, syscall.MS_MANDLOCK},
|
||||||
|
"nomand": {true, syscall.MS_MANDLOCK},
|
||||||
|
"atime": {true, syscall.MS_NOATIME},
|
||||||
|
"noatime": {false, syscall.MS_NOATIME},
|
||||||
|
"diratime": {true, syscall.MS_NODIRATIME},
|
||||||
|
"nodiratime": {false, syscall.MS_NODIRATIME},
|
||||||
|
"bind": {false, syscall.MS_BIND},
|
||||||
|
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
||||||
|
"relatime": {false, syscall.MS_RELATIME},
|
||||||
|
"norelatime": {true, syscall.MS_RELATIME},
|
||||||
|
"strictatime": {false, syscall.MS_STRICTATIME},
|
||||||
|
"nostrictatime": {true, syscall.MS_STRICTATIME},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range strings.Split(m.Options, ",") {
|
||||||
|
// If the option does not exist in the flags table then it is a
|
||||||
|
// data value for a specific fs type
|
||||||
|
if f, exists := flags[o]; exists {
|
||||||
|
if f.clear {
|
||||||
|
flag &= ^f.flag
|
||||||
|
} else {
|
||||||
|
flag |= f.flag
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = append(data, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Mount(m.Device, target, m.Type, uintptr(flag), strings.Join(data, ",")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mount) Unmount(root string) error {
|
||||||
|
target := path.Join(root, m.Target)
|
||||||
|
if mounted, err := Mounted(target); err != nil || !mounted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.Unmount(target, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mounted(mountpoint string) (bool, error) {
|
||||||
|
mntpoint, err := os.Stat(mountpoint)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
parent, err := os.Stat(path.Join(mountpoint, ".."))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
|
||||||
|
parentSt := parent.Sys().(*syscall.Stat_t)
|
||||||
|
|
||||||
|
return mntpointSt.Dev != parentSt.Dev, nil
|
||||||
|
}
|
||||||
|
|
|
@ -21,12 +21,6 @@ lxc.network.mtu = 1500
|
||||||
{{$ROOTFS := .RootfsPath}}
|
{{$ROOTFS := .RootfsPath}}
|
||||||
lxc.rootfs = {{$ROOTFS}}
|
lxc.rootfs = {{$ROOTFS}}
|
||||||
|
|
||||||
{{if and .HostnamePath .HostsPath}}
|
|
||||||
# enable domain name support
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces .HostnamePath}} {{escapeFstabSpaces $ROOTFS}}/etc/hostname none bind,ro 0 0
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces .HostsPath}} {{escapeFstabSpaces $ROOTFS}}/etc/hosts none bind,ro 0 0
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
# use a dedicated pts for the container (and limit the number of pseudo terminal
|
# use a dedicated pts for the container (and limit the number of pseudo terminal
|
||||||
# available)
|
# available)
|
||||||
lxc.pts = 1024
|
lxc.pts = 1024
|
||||||
|
@ -74,32 +68,20 @@ lxc.cgroup.devices.allow = c 10:200 rwm
|
||||||
# standard mount point
|
# standard mount point
|
||||||
# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
|
# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
|
||||||
lxc.pivotdir = lxc_putold
|
lxc.pivotdir = lxc_putold
|
||||||
|
|
||||||
|
# NOTICE: These mounts must be applied within the namespace
|
||||||
|
|
||||||
# WARNING: procfs is a known attack vector and should probably be disabled
|
# WARNING: procfs is a known attack vector and should probably be disabled
|
||||||
# if your userspace allows it. eg. see http://blog.zx2c4.com/749
|
# if your userspace allows it. eg. see http://blog.zx2c4.com/749
|
||||||
lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
||||||
# WARNING: sysfs is a known attack vector and should probably be disabled
|
|
||||||
# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
|
# WARNING: sysfs is a known attack vector and should probably be disabled
|
||||||
|
# if your userspace allows it. eg. see http://bit.ly/T9CkqJ
|
||||||
lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
||||||
|
|
||||||
lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
||||||
#lxc.mount.entry = varrun {{escapeFstabSpaces $ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
|
|
||||||
#lxc.mount.entry = varlock {{escapeFstabSpaces $ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
|
|
||||||
lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
||||||
|
|
||||||
# Inject dockerinit
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces .SysInitPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerinit none bind,ro 0 0
|
|
||||||
|
|
||||||
# Inject env
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces .EnvConfigPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerenv none bind,ro 0 0
|
|
||||||
|
|
||||||
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces .ResolvConfPath}} {{escapeFstabSpaces $ROOTFS}}/etc/resolv.conf none bind,ro 0 0
|
|
||||||
{{if .Volumes}}
|
|
||||||
{{ $rw := .VolumesRW }}
|
|
||||||
{{range $virtualPath, $realPath := .Volumes}}
|
|
||||||
lxc.mount.entry = {{escapeFstabSpaces $realPath}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if (getHostConfig .).Privileged}}
|
{{if (getHostConfig .).Privileged}}
|
||||||
{{if (getCapabilities .).AppArmor}}
|
{{if (getCapabilities .).AppArmor}}
|
||||||
lxc.aa_profile = unconfined
|
lxc.aa_profile = unconfined
|
||||||
|
|
67
runtime.go
67
runtime.go
|
@ -486,10 +486,8 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
|
||||||
hostConfig: &HostConfig{},
|
hostConfig: &HostConfig{},
|
||||||
Image: img.ID, // Always use the resolved image id
|
Image: img.ID, // Always use the resolved image id
|
||||||
NetworkSettings: &NetworkSettings{},
|
NetworkSettings: &NetworkSettings{},
|
||||||
// FIXME: do we need to store this in the container?
|
Name: name,
|
||||||
SysInitPath: runtime.sysInitPath,
|
Driver: runtime.driver.String(),
|
||||||
Name: name,
|
|
||||||
Driver: runtime.driver.String(),
|
|
||||||
}
|
}
|
||||||
container.root = runtime.containerRoot(container.ID)
|
container.root = runtime.containerRoot(container.ID)
|
||||||
// Step 1: create the container directory.
|
// Step 1: create the container directory.
|
||||||
|
@ -790,6 +788,67 @@ func (runtime *Runtime) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (runtime *Runtime) getMounts(container *Container) ([]*graphdriver.Mount, error) {
|
||||||
|
// Generate additional bind mounts
|
||||||
|
envPath, err := container.EnvConfigPath()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mounts := []*graphdriver.Mount{
|
||||||
|
{
|
||||||
|
Device: runtime.sysInitPath,
|
||||||
|
Target: "/.dockerinit",
|
||||||
|
Type: "none",
|
||||||
|
Options: "bind,ro",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: envPath,
|
||||||
|
Target: "/.dockerenv",
|
||||||
|
Type: "none",
|
||||||
|
Options: "bind,ro",
|
||||||
|
},
|
||||||
|
// In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
|
||||||
|
{
|
||||||
|
Device: container.ResolvConfPath,
|
||||||
|
Target: "/etc/resolv.conf",
|
||||||
|
Type: "none",
|
||||||
|
Options: "bind,ro",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if container.HostnamePath != "" && container.HostsPath != "" {
|
||||||
|
mounts = append(mounts,
|
||||||
|
&graphdriver.Mount{
|
||||||
|
Device: container.HostnamePath,
|
||||||
|
Target: "/etc/hostname",
|
||||||
|
Type: "none",
|
||||||
|
Options: "bind,ro",
|
||||||
|
},
|
||||||
|
&graphdriver.Mount{
|
||||||
|
Device: container.HostsPath,
|
||||||
|
Target: "/etc/hosts",
|
||||||
|
Type: "none",
|
||||||
|
Options: "bind,ro",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for r, v := range container.Volumes {
|
||||||
|
mountAs := "ro"
|
||||||
|
if container.VolumesRW[v] {
|
||||||
|
mountAs = "rw"
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts = append(mounts,
|
||||||
|
&graphdriver.Mount{
|
||||||
|
Device: v,
|
||||||
|
Target: r,
|
||||||
|
Type: "none",
|
||||||
|
Options: fmt.Sprintf("bind,%s", mountAs),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (runtime *Runtime) Mount(container *Container) error {
|
func (runtime *Runtime) Mount(container *Container) error {
|
||||||
dir, err := runtime.driver.Get(container.ID)
|
dir, err := runtime.driver.Get(container.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue