Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tmpfs as a valid volume source command. #13587

Merged
merged 1 commit into from
Dec 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ RUN apt-get update && apt-get install -y \
ubuntu-zfs \
xfsprogs \
libzfs-dev \
tar \
--no-install-recommends \
&& ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \
&& ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++
Expand Down
3 changes: 2 additions & 1 deletion contrib/completion/bash/docker
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,7 @@ _docker_run() {
--restart
--security-opt
--stop-signal
--tmpfs
--ulimit
--user -u
--uts
Expand Down Expand Up @@ -1443,7 +1444,7 @@ _docker_run() {
_filedir
return
;;
--device|--volume|-v)
--device|--tmpfs|--volume|-v)
case "$cur" in
*:*)
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/fish/docker.fish
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'P
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l tmpfs -d 'Mount tmpfs on a directory'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/zsh/_docker
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ __docker_subcommand() {
"($help)*--security-opt=[Security options]:security option: "
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
"($help -u --user)"{-u=,--user=}"[Username or UID]:user:_users"
"($help)--tmpfs[mount tmpfs] "
"($help)*-v[Bind mount a volume]:volume: "
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
Expand Down
12 changes: 12 additions & 0 deletions daemon/container_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -1534,3 +1534,15 @@ func (container *Container) unmountVolumes(forceSyscall bool) error {

return nil
}

func (container *Container) tmpfsMounts() []execdriver.Mount {
var mounts []execdriver.Mount
for dest, data := range container.hostConfig.Tmpfs {
mounts = append(mounts, execdriver.Mount{
Source: "tmpfs",
Destination: dest,
Data: data,
})
}
return mounts
}
4 changes: 4 additions & 0 deletions daemon/container_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ func (container *Container) ipcMounts() []execdriver.Mount {
return nil
}

func (container *Container) tmpfsMounts() []execdriver.Mount {
return nil
}

func getDefaultRouteMtu() (int, error) {
return -1, errSystemNotSupported
}
Expand Down
1 change: 1 addition & 0 deletions daemon/execdriver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,5 @@ type CommonCommand struct {
Resources *Resources `json:"resources"`
Rootfs string `json:"rootfs"` // root fs of the container
WorkingDir string `json:"working_dir"`
TmpDir string `json:"tmpdir"` // Directory used to store docker tmpdirs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be private

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm, it's already abused in execdriver

}
1 change: 1 addition & 0 deletions daemon/execdriver/driver_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Mount struct {
Writable bool `json:"writable"`
Private bool `json:"private"`
Slave bool `json:"slave"`
Data string `json:"data"`
}

// Resources contains all resource configs for a driver.
Expand Down
33 changes: 33 additions & 0 deletions daemon/execdriver/native/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ package native

import (
"fmt"
"path/filepath"
"strings"
"syscall"

"github.com/docker/docker/daemon/execdriver"
derr "github.com/docker/docker/errors"
"github.com/docker/docker/pkg/mount"

"github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/configs"
Expand Down Expand Up @@ -288,6 +291,36 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e
container.Mounts = defaultMounts

for _, m := range c.Mounts {
for _, cm := range container.Mounts {
if cm.Destination == m.Destination {
return derr.ErrorCodeMountDup.WithArgs(m.Destination)
}
}

if m.Source == "tmpfs" {
var (
data = "size=65536k"
flags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
err error
)
fulldest := filepath.Join(c.Rootfs, m.Destination)
if m.Data != "" {
flags, data, err = mount.ParseTmpfsOptions(m.Data)
if err != nil {
return err
}
}
container.Mounts = append(container.Mounts, &configs.Mount{
Source: m.Source,
Destination: m.Destination,
Data: data,
Device: "tmpfs",
Flags: flags,
PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination),
PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination),
})
continue
}
flags := syscall.MS_BIND | syscall.MS_REC
if !m.Writable {
flags |= syscall.MS_RDONLY
Expand Down
8 changes: 8 additions & 0 deletions daemon/execdriver/native/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package native
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -128,6 +129,13 @@ type execOutput struct {
// it calls libcontainer APIs to run a container.
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
destroyed := false
var err error
c.TmpDir, err = ioutil.TempDir("", c.ID)
if err != nil {
return execdriver.ExitStatus{ExitCode: -1}, err
}
defer os.RemoveAll(c.TmpDir)

// take the Command and populate the libcontainer.Config from it
container, err := d.createContainer(c, hooks)
if err != nil {
Expand Down
56 changes: 56 additions & 0 deletions daemon/execdriver/native/tmpfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package native

import (
"fmt"
"os"
"os/exec"
"strings"

"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/configs"
)

func genTmpfsPremountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
var premount []configs.Command
tarPath, err := exec.LookPath("tar")
if err != nil {
logrus.Warn("tar command is not available for tmpfs mount: %s", err)
return premount
}
if _, err = exec.LookPath("rm"); err != nil {
logrus.Warn("rm command is not available for tmpfs mount: %s", err)
return premount
}
tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
if _, err := os.Stat(fullDest); err == nil {
premount = append(premount, configs.Command{
Path: tarPath,
Args: []string{"-cf", tarFile, "-C", fullDest, "."},
})
}
return premount
}

func genTmpfsPostmountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
var postmount []configs.Command
tarPath, err := exec.LookPath("tar")
if err != nil {
return postmount
}
rmPath, err := exec.LookPath("rm")
if err != nil {
return postmount
}
if _, err := os.Stat(fullDest); os.IsNotExist(err) {
return postmount
}
tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
postmount = append(postmount, configs.Command{
Path: tarPath,
Args: []string{"-xf", tarFile, "-C", fullDest, "."},
})
return append(postmount, configs.Command{
Path: rmPath,
Args: []string{"-f", tarFile},
})
}
1 change: 1 addition & 0 deletions daemon/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
return err
}
mounts = append(mounts, container.ipcMounts()...)
mounts = append(mounts, container.tmpfsMounts()...)

container.command.Mounts = mounts
if err := daemon.waitForStart(container); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion daemon/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
}

if binds[bind.Destination] {
return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination)
return derr.ErrorCodeMountDup.WithArgs(bind.Destination)
}

if len(bind.Name) > 0 && len(bind.Driver) > 0 {
Expand Down
8 changes: 8 additions & 0 deletions docs/reference/commandline/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ flag exists to allow special use-cases, like running Docker within Docker.
The `-w` lets the command being executed inside directory given, here
`/path/to/dir/`. If the path does not exists it is created inside the container.

### mount tmpfs (--tmpfs)

$ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image

The --tmpfs flag mounts a tmpfs into the container with the rw,noexec,nosuid,size=65536k options.

Underlying content from the /run in the my_image image is copied into tmpfs.

### Mount volume (-v, --read-only)

$ docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
Expand Down
8 changes: 8 additions & 0 deletions docs/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,14 @@ above, or already defined by the developer with a Dockerfile `ENV`:

Similarly the operator can set the **hostname** with `-h`.

### TMPFS (mount tmpfs filesystems)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rhatdan Thank you for the contribution. I think you need to update reference/commandline/run.md with this flag too.

As for this, the break is a bit awkward. Also, I think it is important to point out where the mount command comes from so folks don't confuse it with a Docker command.

    --tmpfs=[]: Create a tmpfs mount with: container-dir[:<options>], where the options are identical to the Linux `mount -t tmpfs -o` command.

    Underlying content from the "container-dir" is copied into tmpfs.

    $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65k my_image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


--tmpfs=[]: Create a tmpfs mount with: container-dir[:<options>], where the options are identical to the Linux `mount -t tmpfs -o` command.

Underlying content from the "container-dir" is copied into tmpfs.

$ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image

### VOLUME (shared filesystems)

-v=[]: Create a bind mount with: [host-src:]container-dest[:<options>], where
Expand Down
10 changes: 5 additions & 5 deletions errors/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,12 +444,12 @@ var (
HTTPStatusCode: http.StatusInternalServerError,
})

// ErrorCodeVolumeDup is generated when we try to mount two volumes
// ErrorCodeMountDup is generated when we try to mount two mounts points
// to the same path.
ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
Value: "VOLUMEDUP",
Message: "Duplicate bind mount '%s'",
Description: "An attempt was made to mount a volume but the specified destination location is already used in a previous mount",
ErrorCodeMountDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
Value: "MOUNTDUP",
Message: "Duplicate mount point '%s'",
Description: "An attempt was made to mount a content but the specified destination location is already used in a previous mount",
HTTPStatusCode: http.StatusInternalServerError,
})

Expand Down
2 changes: 1 addition & 1 deletion integration-cli/docker_api_containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) {
status, body, err := sockRequest("POST", "/containers/"+name+"/start", config)
c.Assert(err, checker.IsNil)
c.Assert(status, checker.Equals, http.StatusInternalServerError)
c.Assert(string(body), checker.Contains, "Duplicate bind", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err))
c.Assert(string(body), checker.Contains, "Duplicate mount point", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err))
}

func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
Expand Down
6 changes: 3 additions & 3 deletions integration-cli/docker_cli_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,10 @@ func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
mountstr2 := path2 + someplace

if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil {
c.Fatal("Expected error about duplicate volume definitions")
c.Fatal("Expected error about duplicate mount definitions")
} else {
if !strings.Contains(out, "Duplicate bind mount") {
c.Fatalf("Expected 'duplicate volume' error, got %v", out)
if !strings.Contains(out, "Duplicate mount point") {
c.Fatalf("Expected 'duplicate mount point' error, got %v", out)
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions integration-cli/docker_cli_run_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,21 @@ func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
c.Assert(err, check.IsNil)
c.Assert(shmSize, check.Equals, "1073741824")
}

func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) {
// TODO Windows (Post TP4): This test cannot run on a Windows daemon as
// Windows does not support tmpfs mounts.
testRequires(c, DaemonIsLinux)
if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil {
c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out)
}
if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil {
c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out)
}
if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil {
c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option")
}
if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil {
c.Fatalf("Should have generated an error saying Duplicate mount points")
}
}
15 changes: 15 additions & 0 deletions man/docker-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ docker-create - Create a new container
[**--stop-signal**[=*SIGNAL*]]
[**--shm-size**[=*[]*]]
[**-t**|**--tty**[=*false*]]
[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]]
[**-u**|**--user**[=*USER*]]
[**--ulimit**[=*[]*]]
[**--uts**[=*[]*]]
Expand Down Expand Up @@ -267,6 +268,20 @@ This value should always larger than **-m**, so you should always use this with
**-t**, **--tty**=*true*|*false*
Allocate a pseudo-TTY. The default is *false*.

**--tmpfs**=[] Create a tmpfs mount

Mount a temporary filesystem (`tmpfs`) mount into a container, for example:

$ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image

This command mounts a `tmpfs` at `/tmp` within the container. The mount copies
the underlying content of `my_image` into `/tmp`. For example if there was a
directory `/tmp/content` in the base image, docker will copy this directory and
all of its content on top of the tmpfs mounted on `/tmp`. The supported mount
options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.

**-u**, **--user**=""
Username or UID

Expand Down
28 changes: 28 additions & 0 deletions man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ docker-run - Run a command in a new container
[**--shm-size**[=*[]*]]
[**--sig-proxy**[=*true*]]
[**-t**|**--tty**[=*false*]]
[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]]
[**-u**|**--user**[=*USER*]]
[**-v**|**--volume**[=*[]*]]
[**--ulimit**[=*[]*]]
Expand Down Expand Up @@ -432,6 +433,20 @@ interactive shell. The default is false.
The **-t** option is incompatible with a redirection of the docker client
standard input.

**--tmpfs**=[] Create a tmpfs mount
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]] 

   Mount a temporary filesystem (tmpfs) mount within a container. For example,
   `--tmpfs /tmp:rw,size=787448k,mode=1777` mounts a tmpfs at /tmp within the
   container. The mount copies the underlying content into the /tmpfs). The
   supported options are the same as the Linux default mount flags
   `rw,noexec,nosuid,nodev,size=65536k`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went a little further on this.

Mount a temporary filesystem (tmpfs) mount within a container. For example, --tmpfs /tmp:rw,size=787448k,mode=1777 mounts a tmpfs at /tmp within the container. The mount copies the underlying content into the tmpfs). The supported options are the same as the Linux default mount flags. If you do not specify any options, we default to the following options: rw,noexec,nosuid,nodev,size=65536k.


Mount a temporary filesystem (`tmpfs`) mount into a container, for example:

$ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image

This command mounts a `tmpfs` at `/tmp` within the container. The mount copies
the underlying content of `my_image` into `/tmp`. For example if there was a
directory `/tmp/content` in the base image, docker will copy this directory and
all of its content on top of the tmpfs mounted on `/tmp`. The supported mount
options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.

**-u**, **--user**=""
Sets the username or UID used and optionally the groupname or GID for the specified command.

Expand Down Expand Up @@ -548,6 +563,19 @@ the exit codes follow the `chroot` standard, see below:

# EXAMPLES

## Running container in read-only mode

During container image development, containers often need to write to the image
content. Installing packages into /usr, for example. In production,
applications seldom need to write to the image. Container applications write
to volumes if they need to write to file systems at all. Applications can be
made more secure by running them in read-only mode using the --read-only switch.
This protects the containers image from modification. Read only containers may
still need to write temporary data. The best way to handle this is to mount
tmpfs directories on /run and /tmp.

# docker run --read-only --tmpfs /run --tmpfs /tmp -i -t fedora /bin/bash

## Exposing log messages from the container to the host's log

If you want messages that are logged in your container to show up in the host's
Expand Down