瀏覽代碼

Merge pull request #1459 from mhennings/set-working-directory

Add an option to set the working directory
Michael Crosby 12 年之前
父節點
當前提交
67c9ce6dd1
共有 5 個文件被更改,包括 125 次插入3 次删除
  1. 63 0
      commands_test.go
  2. 23 0
      container.go
  3. 8 3
      docs/sources/api/docker_remote_api_v1.4.rst
  4. 20 0
      docs/sources/commandline/command/run.rst
  5. 11 0
      sysinit.go

+ 63 - 0
commands_test.go

@@ -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 - 0
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)

+ 8 - 3
docs/sources/api/docker_remote_api_v1.4.rst

@@ -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
 	   }

+ 20 - 0
docs/sources/commandline/command/run.rst

@@ -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 - 0
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())
 }