Selaa lähdekoodia

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

Add an option to set the working directory
Michael Crosby 12 vuotta sitten
vanhempi
commit
67c9ce6dd1

+ 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) {
 func TestRunExit(t *testing.T) {
 	stdin, stdinPipe := io.Pipe()
 	stdin, stdinPipe := io.Pipe()
 	stdout, stdoutPipe := io.Pipe()
 	stdout, stdoutPipe := io.Pipe()

+ 23 - 0
container.go

@@ -2,6 +2,7 @@ package docker
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"errors"
 	"flag"
 	"flag"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/term"
 	"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)
 	Image           string // Name of the image as it was passed by the operator (eg. could be symbolic)
 	Volumes         map[string]struct{}
 	Volumes         map[string]struct{}
 	VolumesFrom     string
 	VolumesFrom     string
+	WorkingDir      string
 	Entrypoint      []string
 	Entrypoint      []string
 	NetworkDisabled bool
 	NetworkDisabled bool
 	Privileged      bool
 	Privileged      bool
@@ -92,6 +94,10 @@ type BindMap struct {
 	Mode    string
 	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) {
 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")
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
 	if len(args) > 0 && args[0] != "--help" {
 	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")
 	flHostname := cmd.String("h", "", "Container host name")
+	flWorkingDir := cmd.String("w", "", "Working directory inside the container")
 	flUser := cmd.String("u", "", "Username or UID")
 	flUser := cmd.String("u", "", "Username or UID")
 	flDetach := cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
 	flDetach := cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id")
 	flAttach := NewAttachOpts()
 	flAttach := NewAttachOpts()
@@ -139,6 +146,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	if *flDetach && len(flAttach) > 0 {
 	if *flDetach && len(flAttach) > 0 {
 		return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
 		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 neither -d or -a are set, attach to everything by default
 	if len(flAttach) == 0 && !*flDetach {
 	if len(flAttach) == 0 && !*flDetach {
 		if !*flDetach {
 		if !*flDetach {
@@ -197,6 +207,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		VolumesFrom:     *flVolumesFrom,
 		VolumesFrom:     *flVolumesFrom,
 		Entrypoint:      entrypoint,
 		Entrypoint:      entrypoint,
 		Privileged:      *flPrivileged,
 		Privileged:      *flPrivileged,
+		WorkingDir:      *flWorkingDir,
 	}
 	}
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{
 		Binds:           binds,
 		Binds:           binds,
@@ -666,6 +677,18 @@ func (container *Container) Start(hostConfig *HostConfig) error {
 		"-e", "container=lxc",
 		"-e", "container=lxc",
 		"-e", "HOSTNAME="+container.Config.Hostname,
 		"-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 {
 	for _, elem := range container.Config.Env {
 		params = append(params, "-e", elem)
 		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,
 		"Dns":null,
 		"Image":"base",
 		"Image":"base",
 		"Volumes":{},
 		"Volumes":{},
-		"VolumesFrom":""
+		"VolumesFrom":"",
+		"WorkingDir":""
+
 	   }
 	   }
 	   
 	   
 	**Example response**:
 	**Example response**:
@@ -195,7 +197,9 @@ Inspect a container
 				"Dns": null,
 				"Dns": null,
 				"Image": "base",
 				"Image": "base",
 				"Volumes": {},
 				"Volumes": {},
-				"VolumesFrom": ""
+				"VolumesFrom": "",
+				"WorkingDir":""
+
 			},
 			},
 			"State": {
 			"State": {
 				"Running": false,
 				"Running": false,
@@ -746,7 +750,8 @@ Inspect an image
 				,"Dns":null,
 				,"Dns":null,
 				"Image":"base",
 				"Image":"base",
 				"Volumes":null,
 				"Volumes":null,
-				"VolumesFrom":""
+				"VolumesFrom":"",
+				"WorkingDir":""
 			},
 			},
 		"Size": 6824592
 		"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.
       -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.
       -volumes-from="": Mount all volumes from the given container.
       -entrypoint="": Overwrite the default entrypoint set by the image.
       -entrypoint="": Overwrite the default entrypoint set by the image.
+      -w="": Working directory inside the container
 
 
 
 
 Examples
 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
 everything that the host can do. This flag exists to allow special
 use-cases, like running Docker within Docker.
 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
 // Takes care of dropping privileges to the desired user
 func changeUser(u string) {
 func changeUser(u string) {
 	if u == "" {
 	if u == "" {
@@ -83,6 +92,7 @@ func SysInit() {
 	}
 	}
 	var u = flag.String("u", "", "username or uid")
 	var u = flag.String("u", "", "username or uid")
 	var gw = flag.String("g", "", "gateway address")
 	var gw = flag.String("g", "", "gateway address")
+	var workdir = flag.String("w", "", "workdir")
 
 
 	var flEnv ListOpts
 	var flEnv ListOpts
 	flag.Var(&flEnv, "e", "Set environment variables")
 	flag.Var(&flEnv, "e", "Set environment variables")
@@ -91,6 +101,7 @@ func SysInit() {
 
 
 	cleanupEnv(flEnv)
 	cleanupEnv(flEnv)
 	setupNetworking(*gw)
 	setupNetworking(*gw)
+	setupWorkingDirectory(*workdir)
 	changeUser(*u)
 	changeUser(*u)
 	executeProgram(flag.Arg(0), flag.Args())
 	executeProgram(flag.Arg(0), flag.Args())
 }
 }