Skip to content

Commit

Permalink
Merge pull request #1087 from dcantah/sandboxmount-wcow
Browse files Browse the repository at this point in the history
Add WCOW sandbox mount support
  • Loading branch information
dcantah authored Sep 24, 2021
2 parents 4275e49 + 92004da commit 1f8211a
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 0 deletions.
3 changes: 3 additions & 0 deletions internal/hcsoci/hcsdoc_wcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ func createMountsConfig(ctx context.Context, coi *createOptionsInternal) (*mount
return nil, err
}
mdv2.HostPath = uvmPath
} else if strings.HasPrefix(mount.Source, "sandbox://") {
// Convert to the path in the guest that was asked for.
mdv2.HostPath = convertToWCOWSandboxMountPath(mount.Source)
} else {
// vsmb mount
uvmPath, err := coi.HostingSystem.GetVSMBUvmPath(ctx, mount.Source, readOnly)
Expand Down
35 changes: 35 additions & 0 deletions internal/hcsoci/resources_wcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ package hcsoci
// Contains functions relating to a WCOW container, as opposed to a utility VM

import (
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/Microsoft/hcsshim/internal/cmd"
"github.com/Microsoft/hcsshim/internal/credentials"
"github.com/Microsoft/hcsshim/internal/devices"
"github.com/Microsoft/hcsshim/internal/layers"
Expand All @@ -23,6 +25,8 @@ import (
"github.com/pkg/errors"
)

const wcowSandboxMountPath = "C:\\SandboxMounts"

func allocateWindowsResources(ctx context.Context, coi *createOptionsInternal, r *resources.Resources, isSandbox bool) error {
if coi.Spec == nil || coi.Spec.Windows == nil || coi.Spec.Windows.LayerFolders == nil {
return errors.New("field 'Spec.Windows.Layerfolders' is not populated")
Expand Down Expand Up @@ -179,6 +183,32 @@ func setupMounts(ctx context.Context, coi *createOptionsInternal, r *resources.R
return errors.Wrapf(err, "adding SCSI EVD mount failed %+v", mount)
}
r.Add(scsiMount)
} else if strings.HasPrefix(mount.Source, "sandbox://") {
// Mounts that map to a path in the UVM are specified with a 'sandbox://' prefix.
//
// Example: sandbox:///a/dirInUvm destination:C:\\dirInContainer.
//
// so first convert to a path in the sandboxmounts path itself.
sandboxPath := convertToWCOWSandboxMountPath(mount.Source)

// Now we need to exec a process in the vm that will make these directories as theres
// no functionality in the Windows gcs to create an arbitrary directory.
//
// Create the directory, but also run dir afterwards regardless of if mkdir succeeded to handle the case where the directory already exists
// e.g. from a previous container specifying the same mount (and thus creating the same directory).
b := &bytes.Buffer{}
stderr, err := cmd.CreatePipeAndListen(b, false)
if err != nil {
return err
}
req := &cmd.CmdProcessRequest{
Args: []string{"cmd", "/c", "mkdir", sandboxPath, "&", "dir", sandboxPath},
Stderr: stderr,
}
exitCode, err := cmd.ExecInUvm(ctx, coi.HostingSystem, req)
if err != nil {
return errors.Wrapf(err, "failed to create sandbox mount directory in utility VM with exit code %d %q", exitCode, b.String())
}
} else {
if uvm.IsPipe(mount.Source) {
pipe, err := coi.HostingSystem.AddPipe(ctx, mount.Source)
Expand All @@ -201,3 +231,8 @@ func setupMounts(ctx context.Context, coi *createOptionsInternal, r *resources.R

return nil
}

func convertToWCOWSandboxMountPath(source string) string {
subPath := strings.TrimPrefix(source, "sandbox://")
return filepath.Join(wcowSandboxMountPath, subPath)
}
124 changes: 124 additions & 0 deletions test/cri-containerd/runpodsandbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,130 @@ func Test_RunPodSandbox_Mount_SandboxDir_LCOW(t *testing.T) {
//TODO: Parse the output of the exec command to make sure the uvm mount was successful
}

func Test_RunPodSandbox_Mount_SandboxDir_WCOW(t *testing.T) {
requireFeatures(t, featureWCOWHypervisor)

pullRequiredImages(t, []string{imageWindowsNanoserver})

client := newTestRuntimeClient(t)
ctx := context.Background()

sbRequest := getRunPodSandboxRequest(t, wcowHypervisorRuntimeHandler, nil)
podID := runPodSandbox(t, client, ctx, sbRequest)
defer removePodSandbox(t, client, ctx, podID)
defer stopPodSandbox(t, client, ctx, podID)

command := []string{
"cmd",
"/c",
"ping",
"-t",
"127.0.0.1",
}

mounts := []*runtime.Mount{
{
HostPath: "sandbox:///test",
ContainerPath: "C:\\test",
},
}
// Create 2 containers with sandbox mounts and verify both can write and see the others files
container1Name := t.Name() + "-Container-" + "1"
container1Id := createContainerInSandbox(t, client, ctx, podID, container1Name, imageWindowsNanoserver, command, nil, mounts, sbRequest.Config)
defer removeContainer(t, client, ctx, container1Id)

startContainer(t, client, ctx, container1Id)
defer stopContainer(t, client, ctx, container1Id)

execEcho := []string{
"cmd",
"/c",
"echo",
`"test"`,
">",
"C:\\test\\test.txt",
}
_, errorMsg, exitCode := execContainer(t, client, ctx, container1Id, execEcho)
if exitCode != 0 {
t.Fatalf("Exec into container failed with: %v and exit code: %d, %s", errorMsg, exitCode, container1Id)
}

container2Name := t.Name() + "-Container-" + "2"
container2Id := createContainerInSandbox(t, client, ctx, podID, container2Name, imageWindowsNanoserver, command, nil, mounts, sbRequest.Config)
defer removeContainer(t, client, ctx, container2Id)

startContainer(t, client, ctx, container2Id)
defer stopContainer(t, client, ctx, container2Id)

// Test that we can see the file made in the first container in the second one.
execDir := []string{
"cmd",
"/c",
"dir",
"C:\\test\\test.txt",
}
_, errorMsg, exitCode = execContainer(t, client, ctx, container2Id, execDir)
if exitCode != 0 {
t.Fatalf("Exec into container failed with: %v and exit code: %d, %s", errorMsg, exitCode, container2Id)
}
}

func Test_RunPodSandbox_Mount_SandboxDir_NoShare_WCOW(t *testing.T) {
requireFeatures(t, featureWCOWHypervisor)

pullRequiredImages(t, []string{imageWindowsNanoserver})

client := newTestRuntimeClient(t)
ctx := context.Background()

sbRequest := getRunPodSandboxRequest(t, wcowHypervisorRuntimeHandler, nil)
podID := runPodSandbox(t, client, ctx, sbRequest)
defer removePodSandbox(t, client, ctx, podID)
defer stopPodSandbox(t, client, ctx, podID)

command := []string{
"cmd",
"/c",
"ping",
"-t",
"127.0.0.1",
}

mounts := []*runtime.Mount{
{
HostPath: "sandbox:///test",
ContainerPath: "C:\\test",
},
}
// This test case is making sure that the sandbox mount doesn't show up in another container if not
// explicitly asked for. Make first container with the mount and another shortly after without.
container1Name := t.Name() + "-Container-" + "1"
container1Id := createContainerInSandbox(t, client, ctx, podID, container1Name, imageWindowsNanoserver, command, nil, mounts, sbRequest.Config)
defer removeContainer(t, client, ctx, container1Id)

startContainer(t, client, ctx, container1Id)
defer stopContainer(t, client, ctx, container1Id)

container2Name := t.Name() + "-Container-" + "2"
container2Id := createContainerInSandbox(t, client, ctx, podID, container2Name, imageWindowsNanoserver, command, nil, nil, sbRequest.Config)
defer removeContainer(t, client, ctx, container2Id)

startContainer(t, client, ctx, container2Id)
defer stopContainer(t, client, ctx, container2Id)

// Test that we can't see the file made in the first container in the second one.
execDir := []string{
"cmd",
"/c",
"dir",
"C:\\test\\",
}
output, _, exitCode := execContainer(t, client, ctx, container2Id, execDir)
if exitCode == 0 {
t.Fatalf("Found directory in second container when not expected: %s", output)
}
}

func Test_RunPodSandbox_CPUGroup(t *testing.T) {
testutilities.RequiresBuild(t, 20124)
ctx := context.Background()
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1f8211a

Please sign in to comment.