Skip to content

LCOW: Prepare work for bind mounts#34258

Merged
vieux merged 1 commit intomoby:masterfrom
simonferquel:lcow-mounts
Sep 14, 2017
Merged

LCOW: Prepare work for bind mounts#34258
vieux merged 1 commit intomoby:masterfrom
simonferquel:lcow-mounts

Conversation

@simonferquel
Copy link
Contributor

This PR modifies mounts validation logic on Windows to accept both
windows container style and linux style destination path (allowing to
run things like docker run -v c:\data:/data alpine ls /data on lcow)

- What I did

Changed a bit the validation of bind mounts specification to accept both windows and linux style destination paths on windows

- How I did it

Changed a bit some regular expressions, tried to be clever about when calling convertFromSlash

- How to verify it

Unit tests are updated accordingly

- Note

Current LCOW implementation does not seem to be able to do bind-mounts for now though. Here is the result of docker run --rm -it -v c:\users:/data busybox:

docker: Error response from daemon: container c2abff68cdfa6dcf97357154d012e37bd874fccb4ccd1517f74bbc51b3905c0b encountered an error during CreateProcess: failure in a Windows system call: Unspecified error (0x80004005) extra info: {"CommandArgs":["sh"],"WorkingDirectory":"/","Environment":{"HOSTNAME":"c2abff68cdfa","PATH":"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","TERM":"xterm"},"EmulateConsole":true,"CreateStdInPipe":true,"CreateStdOutPipe":true,"ConsoleSize":[30,120],"OCISpecification":{"ociVersion":"1.0.0-rc5-dev","platform":{"os":"linux","arch":"amd64"},"process":{"terminal":true,"consoleSize":{"height":30,"width":120},"user":{"uid":0,"gid":0},"args":["sh"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=c2abff68cdfa","TERM=xterm"],"cwd":"/","capabilities":{"bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"inheritable":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"]}},"root":{"path":"rootfs"},"hostname":"c2abff68cdfa","mounts":[{"destination":"/proc","type":"proc","source":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","type":"tmpfs","source":"tmpfs","options":["nosuid","strictatime","mode=755"]},{"destination":"/dev/pts","type":"devpts","source":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/sys","type":"sysfs","source":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/sys/fs/cgroup","type":"cgroup","source":"cgroup","options":["ro","nosuid","noexec","nodev"]},{"destination":"/dev/mqueue","type":"mqueue","source":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/data","source":"c:\\users"}],"linux":{"resources":{"devices":[{"allow":false,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":5,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":3,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":9,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":8,"access":"rwm"},{"allow":true,"type":"c","major":5,"minor":0,"access":"rwm"},{"allow":true,"type":"c","major":5,"minor":1,"access":"rwm"},{"allow":false,"type":"c","major":10,"minor":229,"access":"rwm"}]},"namespaces":[{"type":"mount"},{"type":"network"},{"type":"uts"},{"type":"pid"},{"type":"ipc"}],"maskedPaths":["/proc/kcore","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug"],"readonlyPaths":["/proc/asound","/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]}}}.

@simonferquel simonferquel force-pushed the lcow-mounts branch 2 times, most recently from 8800647 to dad6c6d Compare July 28, 2017 08:44
@simonferquel
Copy link
Contributor Author

Hmm this breaks many dockerfiles with volumes. I suppose we should have a different parsing mechanism for volume paths in dockerfiles (which by nature should only contain a target path and should be validated elsewise)

@jstarks
Copy link
Contributor

jstarks commented Jul 31, 2017

@jhowardmsft PTAL

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me that this validation should be context sensitive. I.e. we should only perform Windows validation when handling mounts for Windows containers, and we should only perform Linux validation when handling mounts for Linux containers.

In the latter case, it would be nice to reuse the existing Linux validation code rather than rewrite it here, although of course that means the shared Linux code would need to use path instead of filepath.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, after having seen CI test failures, I agree that we must do different parsing / validation logic to handle linux containers and windows containers.
However, we can´t leverage existing linux parsing/validation completely, as the source path is still windows style, and the linux parsing code is based on the assumption that : is a forbidden character in a file path...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, I think that after all we need a far more invasive change, and we could take the occasion to refactor a bit the volume package.

I'd like to introduce a volume.Parser interface that would expose methods to parse/validate mount specs, with a constructor taking a platform string. We would have 3 implementaions (linuxParser, windowsParser, lcowParser) that would certainly share some amount of code, instead of relying on conditional compilation and current tricks (for exemple the convertSlashes doing the exact oposite on windows and linux is SO confusing on code maintenance mode...)

@simonferquel
Copy link
Contributor Author

@johnstep, @jhowardmsft, I just completely changed the approach, with a complete volume refactoring (that will require some refinements), where parsing/validation logic depends on the containerPlatform and the host OS. I also removed static compilation switches, to have an easier code reading experience (there were some dirty tricks behind compilation switches), and make it easier to share more code in the future.

@simonferquel
Copy link
Contributor Author

Ok, latest CI failures where essentially stuff related to existing bugs (on certain cases, some checks were not done the same way if ParseMountSpec or ParseMountRaw were called...). I think we can discuss during the design review if we should reproduce the little bugs for the sake of behavior stability or if we want to have them fixed, at the risk of breaking compatibility

@PatrickLang
Copy link

I talked to @adburch and simple paths such as /data should be working in the recent insider releases such as 16257. What Windows build are you testing on?

Paths containing non-Windows allowed characters and / path separators such as /opt/usr/share/foo will need build 16263+

@simonferquel
Copy link
Contributor Author

I am running 10.0.16257.1

Microsoft Windows [Version 10.0.16257.1]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Users\simon>docker run --rm -v c:\:/data busybox ls /
docker: Error response from daemon: container dcaa82082ece5fb245eee0745d5fcd02ccec0733052e88735f7d9c8f4072d5dd encountered an error during CreateProcess: failure in a Windows system call: Unspecified error (0x80004005) extra info: {"CommandArgs":["ls","/"],"WorkingDirectory":"/","Environment":{"HOSTNAME":"dcaa82082ece","PATH":"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},"CreateStdInPipe":true,"CreateStdOutPipe":true,"CreateStdErrPipe":true,"ConsoleSize":[0,0],"OCISpecification":{"ociVersion":"1.0.0-rc5-dev","platform":{"os":"linux","arch":"amd64"},"process":{"consoleSize":{"height":0,"width":0},"user":{"uid":0,"gid":0},"args":["ls","/"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=dcaa82082ece"],"cwd":"/","capabilities":{"bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"inheritable":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"]}},"root":{"path":"rootfs"},"hostname":"dcaa82082ece","mounts":[{"destination":"/proc","type":"proc","source":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","type":"tmpfs","source":"tmpfs","options":["nosuid","strictatime","mode=755"]},{"destination":"/dev/pts","type":"devpts","source":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/sys","type":"sysfs","source":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/sys/fs/cgroup","type":"cgroup","source":"cgroup","options":["ro","nosuid","noexec","nodev"]},{"destination":"/dev/mqueue","type":"mqueue","source":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/data","source":"c:\\"}],"linux":{"resources":{"devices":[{"allow":false,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":5,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":3,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":9,"access":"rwm"},{"allow":true,"type":"c","major":1,"minor":8,"access":"rwm"},{"allow":true,"type":"c","major":5,"minor":0,"access":"rwm"},{"allow":true,"type":"c","major":5,"minor":1,"access":"rwm"},{"allow":false,"type":"c","major":10,"minor":229,"access":"rwm"}]},"namespaces":[{"type":"mount"},{"type":"network"},{"type":"uts"},{"type":"pid"},{"type":"ipc"}],"maskedPaths":["/proc/kcore","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug"],"readonlyPaths":["/proc/asound","/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]}}}.

C:\Users\simon>

Copy link
Member

Choose a reason for hiding this comment

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

delete commented out code and the above comment?

volume/parser.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

I believe the return value here could be just error, it seems that false is only returned with error, so it doesn't need to have both values. It could be just ValidateVolumeName(name string) error.

I realize this was previously implemented this way, but probably doesn't hurt to fix it now that this is being refactored?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I agree, I'll just have to check on error type instead of the combination of both bool and error (which is confusing)

Copy link
Member

Choose a reason for hiding this comment

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

This surprised me. There are definitely restrictions for the volume name on linux (must be at least 3 characters and start with a letter, I believe).

Is that validation happening somewhere else? Maybe we can fix that here as well?

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 think I followed the codepath correctly on this one. I'll double check (the code paths were very complicated before)

volume/parser.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

I was really surprised to find such a large interface, but after looking over the rest of the PR I see that this logic really is scattered all over so there's no easy way to reduce this interface. Hopefully over time we can refactor the callers and reduce the size of this interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I agree. I started with 2 methods, and I progressively had to add a lot of stuff in it...

Copy link
Member

Choose a reason for hiding this comment

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

Normally I believe underscores are used in go filenames instead of .. I'm guessing this was done because parser_linux.go would imply a linux build tag (which we don't want). Maybe parser_lcow.go and parser_linux_volume.go ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I used dots to avoid automatic conditional compilation

@dnephin
Copy link
Member

dnephin commented Aug 10, 2017

Also, I really like this approach!

Design LGTM

Copy link
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

couple other small comments/question on implementation

Copy link
Member

Choose a reason for hiding this comment

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

Do you think it would make sense to move this default into NewParser() ?

I guess a lot of code is calling NewParser(runtime.GOOS), but it could call NewParser("") instead and handle the default case there.

NewParser() would check if the passed parameter is the empty string, and default to runtime.GOOS.

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 initially forced the parameter passing to identify where the volume package was called and to try to identify where there could be problems (especially code validating mounts config where we don't yet have access to the image target OS).

Copy link
Member

Choose a reason for hiding this comment

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

nit: move this down to line 511 so it's next to the code that uses it, instead of stranded up here near unrelated code

Copy link
Member

Choose a reason for hiding this comment

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

Outside of the context of this PR this comment isn't going to make any sense to the reader. I think it should be removed.

volume/parser.go Outdated
Copy link
Contributor

Choose a reason for hiding this comment

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

These are operating systems. A platform carries architecture, as well.

We'll have to be careful about how we export this model.

cc @jhowardmsft @estesp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I suppose volume parsing logic will only be os-specific and architectures should not be taken into account. I'll rename those accordingly.

@simonferquel simonferquel force-pushed the lcow-mounts branch 3 times, most recently from 7f50dae to 039450c Compare August 29, 2017 07:11
@lowenna
Copy link
Member

lowenna commented Sep 7, 2017

Unfortunately for various legal complications I can't. However @johnstep now has one.

@lowenna
Copy link
Member

lowenna commented Sep 7, 2017

@simonferquel

Can you squash your commits, then cherry-pick microsoft@b9abc83 from branch https://github.com/Microsoft/docker/tree/lcow-mounts.

This also depends on a revendor of HCSShim in #34771 (status 4/merge currently).

One word of caution when testing - due to a bug in opengcs (being tracked by microsoft/opengcs#131), the contents of any mapped directory will be deleted on the host on container exit currently.

Example of bind-mounting a read-only directory:

PS E:\go\src\github.com\docker\docker> docker run --rm -it -v c:\john:/target:ro busybox sh
/ # ls -l /target
total 0
-r-xr-xr-x    2 root     root            10 Sep  7 19:30 foo.bar
/ # rm /target/foo.bar
rm: remove '/target/foo.bar'? y
rm: can't remove '/target/foo.bar': Read-only file system
/ #

@simonferquel
Copy link
Contributor Author

@jhowardmsft done. Effectively the final rm -rf stuff is a bit brutal

@tiborvass
Copy link
Contributor

LGTM

@lowenna
Copy link
Member

lowenna commented Sep 13, 2017

@simonferquel A few minor nits, but generally LGTM. And works :)

Copy link
Member

Choose a reason for hiding this comment

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

I think we should (I have made a start in another PR) use "operatingSystem" or "os" rather than platform to indicate in the code base that it's not platform in the fuller sense os/arch/variant. Certainly not plat though.

Copy link
Member

Choose a reason for hiding this comment

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

Don't use lcow in an error message. Linux containers on Windows do not...

Copy link
Member

Choose a reason for hiding this comment

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

The file naming here seems very strange. I don't think we have a precedent for this type of naming anywhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The thing is that for future-proof multi-arch/os system, this PR makes all flavors of the parser available on all OS. I also wanted file ordering make different parsers appear together (so they all have a parser_ prefix. However, if I chose to keep names as parser_linux.go / parser_windows.go it would have triggered conditional compilation, so I had to add a new suffix for avoiding it to happen.

Copy link
Member

Choose a reason for hiding this comment

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

Another workaround is - instead of _ but that is inconsistent with most of the file names in the project. Yet another is to modify the platform name so it will not match exactly, but I can't come up with a satisfactory modification.

If not for the file ordering issue, which seems minor to me, I would name them lcow_parser.go, linux_parser.go, and windows_parser.go, since that is closer to the respective type names.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@johnstep @jhowardmsft just did that. With the small number of files in this package, I suppose it is ok to have them not grouped together.

@simonferquel
Copy link
Contributor Author

@jhowardmsft, I fixed the items in your comments, thanks!

@lowenna
Copy link
Member

lowenna commented Sep 13, 2017

LGTM (can you squash/tidy the commits again please)

@simonferquel
Copy link
Contributor Author

@jhowardmsft, just rebased and squashed

@simonferquel
Copy link
Contributor Author

Errors seem related to the manifest list stuff... should I rebase once again ?

@yongtang
Copy link
Member

@simonferquel If possible can you do a rebase against the master? I think the Jenkins errors has been fixed in #34837. Once the PR is rebased it should be ready to merge.

@lowenna
Copy link
Member

lowenna commented Sep 14, 2017

I restarted PPC/Z. They should now pick up and apply this change on top of master.

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
@vieux
Copy link
Contributor

vieux commented Sep 14, 2017

@jhowardmsft sorry john, I didn't see your comment, I just pushed a rebase against master

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/lcow Issues and PR's related to the experimental LCOW feature status/4-merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.