Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed volume mount issue(759) for Windows #924

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

click2cloud-lamda
Copy link
Contributor

@click2cloud-lamda click2cloud-lamda commented Mar 23, 2022

Fixed issue #759 :

  • volume binding issue in Windows containers
D:\GolandProjects\github.com\nerdctl2\cmd\nerdctl>nerdctl run -it -v C:\ProgramData\nerdctl\052055e3\volumes\default\test\_data:c:\src mcr.microsoft.com/windows/nanoserver:20H2
Microsoft Windows [Version 10.0.19042.1586]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Windows\system32>cd ../..

C:\>dir
 Volume in drive C has no label.
 Volume Serial Number is 7643-AF54

 Directory of C:\

03/03/2022  01:27 PM             5,510 License.txt
03/16/2022  07:00 PM    <DIR>          src
03/16/2022  07:50 PM    <DIR>          Users
03/16/2022  07:50 PM    <DIR>          Windows
               1 File(s)          5,510 bytes
               3 Dir(s)  21,302,005,760 bytes free

C:\>

When using nerdctl + containerd on Windows, I am able to mount volumes or bind mount to a new windows container.

Signed-off-by: Raymond Mathew click2cloud-lamda@click2cloud.net
Signed-off-by: Dasara Nagaraju dasara.nagaraju@click2cloud.net

go.mod Outdated
@@ -118,6 +118,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/moby v20.10.13+incompatible // indirect
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure should we add a full moby dependency here?

@@ -66,3 +74,65 @@ func ProcessFlagTmpfs(s string) (*Processed, error) {
func ProcessFlagMount(s string, volStore volumestore.VolumeStore) (*Processed, error) {
return nil, errdefs.ErrNotImplemented
}
func ProcessSplit(s string, volStore volumestore.VolumeStore) (Processed, error) {
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 the ProcessSpllit code is as same as the code in mountutil_linux.go. FYI https://github.com/containerd/nerdctl/pull/924/files#diff-ab0f47dd8154315baddf1347290f120ed5e9975a660c4bfb838d17dcd2418c50R434

Maybe we can add a new file named mountutils_otthers.go and add //go:build !windows at the top of the code and move ProcessSpllit here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or maybe we can keep the conditions of ProcessSplit for linux and freebsd in mountutil.go and keep the windows one as it is.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think it's a better choice


var (
res Processed
src, dst string
Copy link
Member

Choose a reason for hiding this comment

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

IMHO, I think using global mutable variables here would not be the best choice.

Would you mind telling me why we need global mutable variables here?

"runtime"
"strings"

"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/oci"
Copy link
Member

Choose a reason for hiding this comment

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

Fix the lint error plz

@AkihiroSuda
Copy link
Member

Commits 25

Please remove irrelevant commits and squash your ones.
Just like other PRs: https://github.com/containerd/nerdctl/pulls?q=is%3Apr+is%3Amerged

Copy link
Member

@Zheaoli Zheaoli left a comment

Choose a reason for hiding this comment

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

Maybe we got some rebase issue here?

Copy link
Member

@Zheaoli Zheaoli left a comment

Choose a reason for hiding this comment

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

Maybe we got some rebase issue here?

@@ -75,7 +75,7 @@ func newBuildCommand() *cobra.Command {
}

func getBuildkitHost(cmd *cobra.Command) (string, error) {
if cmd.Flags().Changed("buildkit-host") {
if cmd.Flags().Changed("buildkit-host") || os.Getenv("BUILDKIT_HOST") != "" {
Copy link
Member

Choose a reason for hiding this comment

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

IMHO, The piece of code here is may belong to the #932

Maybe we got some rebase issue here?

@@ -439,3 +451,52 @@ func NetworkFromNative(n *native.Network) (*Network, error) {

return &res, nil
}

type Specs struct {
Copy link
Member

Choose a reason for hiding this comment

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

@@ -422,3 +422,7 @@ func getTmpfsSize(size int64) string {

return fmt.Sprintf("size=%d%s", size, suffix)
}
func ProcessSplit(s string, volStore volumestore.VolumeStore, res Processed, src string, dst string, options []string) (string, string, []string, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Needs godoc

@@ -68,3 +71,81 @@ func ProcessFlagTmpfs(s string) (*Processed, error) {
func ProcessFlagMount(s string, volStore volumestore.VolumeStore) (*Processed, error) {
return nil, errdefs.ErrNotImplemented
}

func ProcessSplit(s string, volStore volumestore.VolumeStore, res Processed, src string, dst string, options []string) (string, string, []string, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Needs godoc.
Also, can we have unit tests?

@@ -106,7 +106,7 @@ type Container struct {
// TODO: SizeRw *int64 `json:",omitempty"`
// TODO: SizeRootFs *int64 `json:",omitempty"`

// TODO: Mounts []MountPoint
Mounts []specs.Mount
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Addressed in #822

"github.com/docker/go-connections/nat"
"github.com/moby/moby/api/types"
Copy link
Member

Choose a reason for hiding this comment

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

We do not import this package. Please see other struct definitions and keep the coding style consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay.

@@ -195,12 +196,26 @@ type NetworkEndpointSettings struct {

// ContainerFromNative instantiates a Docker-compatible Container from containerd-native Container.
func ContainerFromNative(n *native.Container) (*Container, error) {
var mounts *specs.Spec
// for unmarshalling mount details
err := json.Unmarshal(n.Container.Spec.Value, &mounts)
Copy link
Member

Choose a reason for hiding this comment

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

We already have specs.Spec. Please see the if sp, ok := n.Spec.(*specs.Spec); ok { line below.

Copy link
Contributor

@jsturtevant jsturtevant left a comment

Choose a reason for hiding this comment

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

sorry for the delay, been on PTO for a couple weeks.

Comment on lines 58 to 57
if runtime.GOOS == "windows" {
sr, ds, optns, err := ProcessSplit(s, volStore, res, src, dst, options)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are adding a check for goos if we are doing the process split logic in os specific files? it feels strange to have the case statements tucked away for Windows but for other OS (linux) process split is in this file especially since process split doesn't do anything for linux/freebsd and are exactly the same

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 will just keep the process split logic to respective os files and remove the os logic.

Comment on lines 70 to 138
func ProcessSplit(s string, volStore volumestore.VolumeStore, res Processed, src string, dst string, options []string) (string, string, []string, error) {
var x []string
return "", "", x, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

freebsd implementation is the same as linux, why do we have it duplicated? (see other comment about the goos check for processsplit in mountutil.go)

"-v", fmt.Sprintf("%s:C:\\mnt3", rwVolName),
"-v", fmt.Sprintf("%s:C:\\mnt4ro", roVolName),
imageName).AssertOK()
base.Cmd("container", "inspect", containerName).AssertOK()
Copy link
Contributor

Choose a reason for hiding this comment

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

this asserts that the container created, do we also want to make sure that the volume is actually there (maybe with something in it)? How is this tested on Linux?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TestRunAnonymousVolume and TestRunNamedVolume checks whether the volume is mounted or not. For linux, we havent added any test as the problem was with the windows part.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we some coverage that the volume is created. Another thing noticed now, is it seem like we are also testing read/write behavior here. I would think we want to verify that the in the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For verifying the read/write behavior, inspect should print the details regarding it. But in inspect for windows, it doesn't work.

D:\GolandProjects\github.com\nerdctlmst\cmd\nerdctl>nerdctl inspect 1f44fa83de
[
    {
        "Id": "1f44fa83de31867ec49d2639a15f4d04fa6003765660afb9e50c51e887e89815",
        "Created": "2022-04-28T11:03:48.0466636Z",
        "Path": "c:\\windows\\system32\\cmd.exe",
        "Args": null,
        "State": {
            "Status": "exited",
            "Running": false,
            "Paused": false,
            "Pid": 25496,
            "ExitCode": 0,
            "FinishedAt": "2022-04-28T11:03:50.2407926Z"
        },
        "Image": "mcr.microsoft.com/windows/nanoserver:20H2",
        "ResolvConfPath": "",
        "HostnamePath": "",
        "LogPath": "",
        "Name": "nanoserver-1f44f",
        "Driver": "windows",
        "Platform": "windows",
        "AppArmorProfile": "",
        "Mounts": [
            {
                "Source": "C:\\ProgramData\\nerdctl\\052055e3\\volumes\\default\\testVolume\\_data",
                "Destination": "C:\\src",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "NetworkSettings": {
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "MacAddress": "",
            "Networks": {}
        }
    }
]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the output Im getting.

Copy link
Contributor

Choose a reason for hiding this comment

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

So the inspect output for the volume is there but the rw/ro info isn't valid? Does the actual directory in the container have the correct attributes? If it is just inspect that isn't return properly lets create an issue to resolve later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This issue is only with windows where I cant get the output for read/write behavior.


// ProcessSplit splits volume mount information received through command line into respective source, destination
// and optsRaw fields according to the volume type chosen and returns them accordingly.
func ProcessSplit(s string, volStore volumestore.VolumeStore, res *Processed, src string, dst string, options []string) (string, string, []string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is exactly the same for freebsd and linux. Can we reduce this so the code isn't copy pasted in two places?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay

options = append(options, "rbind")
}
res.Mount = specs.Mount{
Type: fstype,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this being dropped? I don't see it moved anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because of this the mounted type during inspect was null. Now I get this output:

D:\GolandProjects\github.com\nerdctl\cmd\nerdctl>nerdctl inspect 3c46c12ed1f1
[
    {
        "Id": "3c46c12ed1f1f6272b4ab3f93996a19e2e52667313c2a2cbbf94f87b08350dad",
        "Created": "2022-05-06T09:54:49.1905099Z",
        "Path": "c:\\windows\\system32\\cmd.exe",
        "Args": null,
        "State": {
            "Status": "exited",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "Pid": 10928,
            "ExitCode": 0,
            "FinishedAt": "2022-05-06T09:54:51.2735242Z"
        },
        "Image": "mcr.microsoft.com/windows/nanoserver:20H2",
        "ResolvConfPath": "",
        "HostnamePath": "",
        "LogPath": "",
        "Name": "nanoserver-3c46c",
        "RestartCount": 0,
        "Driver": "windows",
        "Platform": "windows",
        "AppArmorProfile": "",
        "Mounts": [
            {
                "Type": "volume",
                "Name": "500907e8f1465b04f132f5a49b76c59c1e41f18aa9063faffa5c2bb7fc2e72a5",
                "Source": "C:\\ProgramData\\nerdctl\\052055e3\\volumes\\default\\500907e8f1465b04f132f5a49b76c59c1e41f18aa9063faffa5c2bb7fc2e72a5\\_data",
                "Destination": "C:\\src",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "NetworkSettings": {
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "MacAddress": "",
            "Networks": {}
        }
    }
]

Please check.

Copy link
Contributor

Choose a reason for hiding this comment

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

is only printing null an issue on Windows? I am concerned this change affects something on freebsd or linux. The tests aren't failing but maybe we are missing coverage here

Copy link
Contributor

Choose a reason for hiding this comment

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

If we pass "none" as fstype for windows, it gives this error: "time="2022-05-12T12:13:13+05:30" level=fatal msg="invalid OCI spec - Type 'none' not supported: unknown"".

why are we passing nullfs or none, when we are already passing mount types like bind, volume or tmps while splitting the source and destination.

I don't know why none/nullfs is passed here. @AkihiroSuda do you have any insight?

options = append(options, "rbind")
}
res.Mount = specs.Mount{
Type: fstype,
Copy link
Contributor

Choose a reason for hiding this comment

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

is only printing null an issue on Windows? I am concerned this change affects something on freebsd or linux. The tests aren't failing but maybe we are missing coverage here

@@ -0,0 +1,100 @@
//go:build freebsd || linux
Copy link
Contributor

Choose a reason for hiding this comment

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

This file should be named mountutil_others.go or mountutil_linux.go to stay consistent that this a function that is specific to runtimes related to mountutil.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we pass "none" as fstype for windows, it gives this error: "time="2022-05-12T12:13:13+05:30" level=fatal msg="invalid OCI spec - Type 'none' not supported: unknown"".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why are we passing nullfs or none, when we are already passing mount types like bind, volume or tmps while splitting the source and destination.

Copy link
Contributor

Choose a reason for hiding this comment

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

These comments look to have landed on the wrong thread. I've responded here: #924 (comment)

click2cloud-lamda and others added 2 commits May 12, 2022 13:05
Signed-off-by: Raymond Mathew <raymond.mathew@click2cloud.net>
@jsturtevant
Copy link
Contributor

I think we've gotten pretty far on this. I would be inclined to merge and iterate. The last comment I have a concern on is https://github.com/containerd/nerdctl/pull/924/files#r871565970. @AkihiroSuda do you know what that fstype is used for and if it is a required for freebsd or linux?

res.Opts = append(res.Opts, specOpts...)
default:
return nil, fmt.Errorf("failed to parse %q", s)
src, dst, options, err = ProcessSplit(s, volStore, &res, src, dst, options)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the implementations, src, dst, and options are never consumed, so should probably not be parameters, but local vars in ProcessSplit. That would allow simplifying this call slightly to src, dst, options, err := ProcessSplit(s, volStore, &res), trimming the top-of-function var lines, rather than growing them.

Also, does it need to be public? processSplit seems like it should work, all uses are in pkg/mountutil AFAICT, and it seems slightly safer to hide platform-split functions to control the blast radius in case of future divergence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform/Windows/Non-WSL2 Microsoft Windows (non-WSL2)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants