Skip to content

Commit 9fa4490

Browse files
author
John Howard
committed
LCOW: WORKDIR correct handling
Signed-off-by: John Howard <[email protected]>
1 parent 8bee1e9 commit 9fa4490

10 files changed

+97
-39
lines changed

builder/dockerfile/dispatchers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ func workdir(req dispatchRequest) error {
387387
runConfig := req.state.runConfig
388388
// This is from the Dockerfile and will not necessarily be in platform
389389
// specific semantics, hence ensure it is converted.
390-
runConfig.WorkingDir, err = normaliseWorkdir(runConfig.WorkingDir, req.args[0])
390+
runConfig.WorkingDir, err = normaliseWorkdir(req.builder.platform, runConfig.WorkingDir, req.args[0])
391391
if err != nil {
392392
return err
393393
}

builder/dockerfile/dispatchers_unix.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// normaliseWorkdir normalises a user requested working directory in a
1313
// platform semantically consistent way.
14-
func normaliseWorkdir(current string, requested string) (string, error) {
14+
func normaliseWorkdir(_ string, current string, requested string) (string, error) {
1515
if requested == "" {
1616
return "", errors.New("cannot normalise nothing")
1717
}

builder/dockerfile/dispatchers_unix_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package dockerfile
44

55
import (
6+
"runtime"
67
"testing"
78
)
89

@@ -16,7 +17,7 @@ func TestNormaliseWorkdir(t *testing.T) {
1617
}
1718

1819
for _, test := range testCases {
19-
normalised, err := normaliseWorkdir(test.current, test.requested)
20+
normalised, err := normaliseWorkdir(runtime.GOOS, test.current, test.requested)
2021

2122
if test.expectedError != "" && err == nil {
2223
t.Fatalf("NormaliseWorkdir should return an error %s, got nil", test.expectedError)

builder/dockerfile/dispatchers_windows.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"os"
7+
"path"
78
"path/filepath"
89
"regexp"
910
"strings"
@@ -15,7 +16,33 @@ var pattern = regexp.MustCompile(`^[a-zA-Z]:\.$`)
1516

1617
// normaliseWorkdir normalises a user requested working directory in a
1718
// platform semantically consistent way.
18-
func normaliseWorkdir(current string, requested string) (string, error) {
19+
func normaliseWorkdir(platform string, current string, requested string) (string, error) {
20+
if platform == "" {
21+
platform = "windows"
22+
}
23+
if platform == "windows" {
24+
return normaliseWorkdirWindows(current, requested)
25+
}
26+
return normaliseWorkdirUnix(current, requested)
27+
}
28+
29+
// normaliseWorkdirUnix normalises a user requested working directory in a
30+
// platform semantically consistent way.
31+
func normaliseWorkdirUnix(current string, requested string) (string, error) {
32+
if requested == "" {
33+
return "", errors.New("cannot normalise nothing")
34+
}
35+
current = strings.Replace(current, string(os.PathSeparator), "/", -1)
36+
requested = strings.Replace(requested, string(os.PathSeparator), "/", -1)
37+
if !path.IsAbs(requested) {
38+
return path.Join(`/`, current, requested), nil
39+
}
40+
return requested, nil
41+
}
42+
43+
// normaliseWorkdirWindows normalises a user requested working directory in a
44+
// platform semantically consistent way.
45+
func normaliseWorkdirWindows(current string, requested string) (string, error) {
1946
if requested == "" {
2047
return "", errors.New("cannot normalise nothing")
2148
}

builder/dockerfile/dispatchers_windows_test.go

+23-17
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,31 @@ package dockerfile
55
import "testing"
66

77
func TestNormaliseWorkdir(t *testing.T) {
8-
tests := []struct{ current, requested, expected, etext string }{
9-
{``, ``, ``, `cannot normalise nothing`},
10-
{``, `C:`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
11-
{``, `C:.`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
12-
{`c:`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
13-
{`c:.`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
14-
{``, `a`, `C:\a`, ``},
15-
{``, `c:\foo`, `C:\foo`, ``},
16-
{``, `c:\\foo`, `C:\foo`, ``},
17-
{``, `\foo`, `C:\foo`, ``},
18-
{``, `\\foo`, `C:\foo`, ``},
19-
{``, `/foo`, `C:\foo`, ``},
20-
{``, `C:/foo`, `C:\foo`, ``},
21-
{`C:\foo`, `bar`, `C:\foo\bar`, ``},
22-
{`C:\foo`, `/bar`, `C:\bar`, ``},
23-
{`C:\foo`, `\bar`, `C:\bar`, ``},
8+
tests := []struct{ platform, current, requested, expected, etext string }{
9+
{"windows", ``, ``, ``, `cannot normalise nothing`},
10+
{"windows", ``, `C:`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
11+
{"windows", ``, `C:.`, ``, `C:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
12+
{"windows", `c:`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
13+
{"windows", `c:.`, `\a`, ``, `c:. is not a directory. If you are specifying a drive letter, please add a trailing '\'`},
14+
{"windows", ``, `a`, `C:\a`, ``},
15+
{"windows", ``, `c:\foo`, `C:\foo`, ``},
16+
{"windows", ``, `c:\\foo`, `C:\foo`, ``},
17+
{"windows", ``, `\foo`, `C:\foo`, ``},
18+
{"windows", ``, `\\foo`, `C:\foo`, ``},
19+
{"windows", ``, `/foo`, `C:\foo`, ``},
20+
{"windows", ``, `C:/foo`, `C:\foo`, ``},
21+
{"windows", `C:\foo`, `bar`, `C:\foo\bar`, ``},
22+
{"windows", `C:\foo`, `/bar`, `C:\bar`, ``},
23+
{"windows", `C:\foo`, `\bar`, `C:\bar`, ``},
24+
{"linux", ``, ``, ``, `cannot normalise nothing`},
25+
{"linux", ``, `foo`, `/foo`, ``},
26+
{"linux", ``, `/foo`, `/foo`, ``},
27+
{"linux", `/foo`, `bar`, `/foo/bar`, ``},
28+
{"linux", `/foo`, `/bar`, `/bar`, ``},
29+
{"linux", `\a`, `b\c`, `/a/b/c`, ``},
2430
}
2531
for _, i := range tests {
26-
r, e := normaliseWorkdir(i.current, i.requested)
32+
r, e := normaliseWorkdir(i.platform, i.current, i.requested)
2733

2834
if i.etext != "" && e == nil {
2935
t.Fatalf("TestNormaliseWorkingDir Expected error %s for '%s' '%s', got no error", i.etext, i.current, i.requested)

container/container.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,17 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error
261261

262262
// SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
263263
func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error {
264+
// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
265+
// We will need to do remote filesystem operations here.
266+
if container.Platform != runtime.GOOS {
267+
return nil
268+
}
269+
264270
if container.Config.WorkingDir == "" {
265271
return nil
266272
}
267273

268274
container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir)
269-
270275
pth, err := container.GetResourcePath(container.Config.WorkingDir)
271276
if err != nil {
272277
return err

daemon/container.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package daemon
33
import (
44
"fmt"
55
"os"
6+
"path"
67
"path/filepath"
8+
"runtime"
9+
"strings"
710
"time"
811

912
containertypes "github.com/docker/docker/api/types/container"
@@ -226,13 +229,24 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
226229

227230
// verifyContainerSettings performs validation of the hostconfig and config
228231
// structures.
229-
func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
230-
232+
func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
231233
// First perform verification of settings common across all platforms.
232234
if config != nil {
233235
if config.WorkingDir != "" {
234-
config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics
235-
if !system.IsAbs(config.WorkingDir) {
236+
wdInvalid := false
237+
if runtime.GOOS == platform {
238+
config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics
239+
if !system.IsAbs(config.WorkingDir) {
240+
wdInvalid = true
241+
}
242+
} else {
243+
// LCOW. Force Unix semantics
244+
config.WorkingDir = strings.Replace(config.WorkingDir, string(os.PathSeparator), "/", -1)
245+
if !path.IsAbs(config.WorkingDir) {
246+
wdInvalid = true
247+
}
248+
}
249+
if wdInvalid {
236250
return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir)
237251
}
238252
}

daemon/create.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,17 @@ func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, manage
3939
return containertypes.ContainerCreateCreatedBody{}, validationError{errors.New("Config cannot be empty in order to create a container")}
4040
}
4141

42-
warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false)
42+
// TODO: @jhowardmsft LCOW support - at a later point, can remove the hard-coding
43+
// to force the platform to be linux.
44+
// Default the platform if not supplied
45+
if params.Platform == "" {
46+
params.Platform = runtime.GOOS
47+
}
48+
if system.LCOWSupported() {
49+
params.Platform = "linux"
50+
}
51+
52+
warnings, err := daemon.verifyContainerSettings(params.Platform, params.HostConfig, params.Config, false)
4353
if err != nil {
4454
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err}
4555
}
@@ -75,16 +85,6 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
7585
err error
7686
)
7787

78-
// TODO: @jhowardmsft LCOW support - at a later point, can remove the hard-coding
79-
// to force the platform to be linux.
80-
// Default the platform if not supplied
81-
if params.Platform == "" {
82-
params.Platform = runtime.GOOS
83-
}
84-
if system.LCOWSupported() {
85-
params.Platform = "linux"
86-
}
87-
8888
if params.Config.Image != "" {
8989
img, err = daemon.GetImage(params.Config.Image)
9090
if err != nil {

daemon/start.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
7979

8080
// check if hostConfig is in line with the current system settings.
8181
// It may happen cgroups are umounted or the like.
82-
if _, err = daemon.verifyContainerSettings(container.HostConfig, nil, false); err != nil {
82+
if _, err = daemon.verifyContainerSettings(container.Platform, container.HostConfig, nil, false); err != nil {
8383
return validationError{err}
8484
}
8585
// Adapt for old containers in case we have updates in this function and

daemon/update.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ import (
1111
func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) {
1212
var warnings []string
1313

14-
warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true)
14+
c, err := daemon.GetContainer(name)
15+
if err != nil {
16+
return container.ContainerUpdateOKBody{Warnings: warnings}, err
17+
}
18+
19+
warnings, err = daemon.verifyContainerSettings(c.Platform, hostConfig, nil, true)
1520
if err != nil {
1621
return container.ContainerUpdateOKBody{Warnings: warnings}, validationError{err}
1722
}

0 commit comments

Comments
 (0)