Add an option to set the working directory.
This makes it possible to simply wrap a command inside a container. This makes it easier to use a container as an unified build environment. Examples: ~/workspace/docker $ docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu ls AUTHORS Makefile archive.go changes.go docker [...] docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd /home/marco/workspace/docker
This commit is contained in:
parent
3885ee00c5
commit
687d27ab57
5 changed files with 125 additions and 3 deletions
|
@ -90,6 +90,69 @@ func TestRunHostname(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory
|
||||
func TestRunWorkdir(t *testing.T) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-w", "/foo/bar", unitTestImageID, "pwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cmdOutput != "/foo/bar\n" {
|
||||
t.Fatalf("'pwd' should display '%s', not '%s'", "/foo/bar\n", cmdOutput)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
||||
<-c
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists
|
||||
func TestRunWorkdirExists(t *testing.T) {
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
if err := cli.CmdRun("-w", "/proc", unitTestImageID, "pwd"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "Reading command output time out", 2*time.Second, func() {
|
||||
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cmdOutput != "/proc\n" {
|
||||
t.Fatalf("'pwd' should display '%s', not '%s'", "/proc\n", cmdOutput)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
|
||||
<-c
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
func TestRunExit(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
|
23
container.go
23
container.go
|
@ -2,6 +2,7 @@ package docker
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/term"
|
||||
|
@ -76,6 +77,7 @@ type Config struct {
|
|||
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
|
||||
Volumes map[string]struct{}
|
||||
VolumesFrom string
|
||||
WorkingDir string
|
||||
Entrypoint []string
|
||||
NetworkDisabled bool
|
||||
Privileged bool
|
||||
|
@ -92,6 +94,10 @@ type BindMap struct {
|
|||
Mode string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvaidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
|
||||
)
|
||||
|
||||
func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
|
||||
cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
|
||||
if len(args) > 0 && args[0] != "--help" {
|
||||
|
@ -100,6 +106,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
|||
}
|
||||
|
||||
flHostname := cmd.String("h", "", "Container host name")
|
||||
flWorkingDir := cmd.String("w", "", "Working directory inside the container")
|
||||
flUser := cmd.String("u", "", "Username or UID")
|
||||
flDetach := cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
|
||||
flAttach := NewAttachOpts()
|
||||
|
@ -139,6 +146,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
|||
if *flDetach && len(flAttach) > 0 {
|
||||
return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
|
||||
}
|
||||
if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
|
||||
return nil, nil, cmd, ErrInvaidWorikingDirectory
|
||||
}
|
||||
// If neither -d or -a are set, attach to everything by default
|
||||
if len(flAttach) == 0 && !*flDetach {
|
||||
if !*flDetach {
|
||||
|
@ -197,6 +207,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
|
|||
VolumesFrom: *flVolumesFrom,
|
||||
Entrypoint: entrypoint,
|
||||
Privileged: *flPrivileged,
|
||||
WorkingDir: *flWorkingDir,
|
||||
}
|
||||
hostConfig := &HostConfig{
|
||||
Binds: binds,
|
||||
|
@ -666,6 +677,18 @@ func (container *Container) Start(hostConfig *HostConfig) error {
|
|||
"-e", "container=lxc",
|
||||
"-e", "HOSTNAME="+container.Config.Hostname,
|
||||
)
|
||||
if container.Config.WorkingDir != "" {
|
||||
workingDir := path.Clean(container.Config.WorkingDir)
|
||||
utils.Debugf("[working dir] working dir is %s", workingDir)
|
||||
|
||||
if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
params = append(params,
|
||||
"-w", workingDir,
|
||||
)
|
||||
}
|
||||
|
||||
for _, elem := range container.Config.Env {
|
||||
params = append(params, "-e", elem)
|
||||
|
|
|
@ -129,7 +129,9 @@ Create a container
|
|||
"Dns":null,
|
||||
"Image":"base",
|
||||
"Volumes":{},
|
||||
"VolumesFrom":""
|
||||
"VolumesFrom":"",
|
||||
"WorkingDir":""
|
||||
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
@ -195,7 +197,9 @@ Inspect a container
|
|||
"Dns": null,
|
||||
"Image": "base",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
"VolumesFrom": "",
|
||||
"WorkingDir":""
|
||||
|
||||
},
|
||||
"State": {
|
||||
"Running": false,
|
||||
|
@ -746,7 +750,8 @@ Inspect an image
|
|||
,"Dns":null,
|
||||
"Image":"base",
|
||||
"Volumes":null,
|
||||
"VolumesFrom":""
|
||||
"VolumesFrom":"",
|
||||
"WorkingDir":""
|
||||
},
|
||||
"Size": 6824592
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume.
|
||||
-volumes-from="": Mount all volumes from the given container.
|
||||
-entrypoint="": Overwrite the default entrypoint set by the image.
|
||||
-w="": Working directory inside the container
|
||||
|
||||
|
||||
Examples
|
||||
|
@ -62,3 +63,22 @@ cgroup controller. In other words, the container can then do almost
|
|||
everything that the host can do. This flag exists to allow special
|
||||
use-cases, like running Docker within Docker.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -w /path/to/dir/ -i -t ubuntu pwd
|
||||
|
||||
The ``-w`` lets the command beeing executed inside directory given,
|
||||
here /path/to/dir/. If the path does not exists it is created inside the
|
||||
container.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
|
||||
|
||||
The ``-v`` flag mounts the current working directory into the container.
|
||||
The ``-w`` lets the command beeing executed inside the current
|
||||
working directory, by changeing into the directory to the value
|
||||
returned by ``pwd``. So this combination executes the command
|
||||
using the container, but inside the current working directory.
|
||||
|
||||
|
||||
|
|
11
sysinit.go
11
sysinit.go
|
@ -22,6 +22,15 @@ func setupNetworking(gw string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Setup working directory
|
||||
func setupWorkingDirectory(workdir string) {
|
||||
if workdir == "" {
|
||||
return
|
||||
}
|
||||
syscall.Chdir(workdir)
|
||||
}
|
||||
|
||||
|
||||
// Takes care of dropping privileges to the desired user
|
||||
func changeUser(u string) {
|
||||
if u == "" {
|
||||
|
@ -83,6 +92,7 @@ func SysInit() {
|
|||
}
|
||||
var u = flag.String("u", "", "username or uid")
|
||||
var gw = flag.String("g", "", "gateway address")
|
||||
var workdir = flag.String("w", "", "workdir")
|
||||
|
||||
var flEnv ListOpts
|
||||
flag.Var(&flEnv, "e", "Set environment variables")
|
||||
|
@ -91,6 +101,7 @@ func SysInit() {
|
|||
|
||||
cleanupEnv(flEnv)
|
||||
setupNetworking(*gw)
|
||||
setupWorkingDirectory(*workdir)
|
||||
changeUser(*u)
|
||||
executeProgram(flag.Arg(0), flag.Args())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue