Skip to content

Commit

Permalink
Support adding mount to running containers
Browse files Browse the repository at this point in the history
- Extend hcsTask.Update() to process and add
mount for running process isolated and hyperV
wcow containers

Signed-off-by: Kirtana Ashok <kiashok@microsoft.com>
  • Loading branch information
kiashok committed Sep 27, 2023
1 parent 4dc2c8b commit 694d751
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 2 deletions.
64 changes: 64 additions & 0 deletions cmd/containerd-shim-runhcs-v1/service_internal_taskshim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"context"
"fmt"
"math/rand"
"os"
"path/filepath"
"strconv"
"testing"
"time"

"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
"github.com/Microsoft/hcsshim/pkg/ctrdtaskapi"
task "github.com/containerd/containerd/api/runtime/task/v2"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/protobuf"
Expand Down Expand Up @@ -536,6 +539,67 @@ func Test_TaskShim_updateInternal_Success(t *testing.T) {
}
}

// Tests if a requested mount is valid for windows containers.
// Currently only host volumes/directories are supported to be mounted
// on a running windows container.
func Test_TaskShimWindowsMount_updateInternal_Success(t *testing.T) {
s, t1, _ := setupTaskServiceWithFakes(t)
t1.isWCOW = true

hostRWSharedDirectory := t.TempDir()
fRW, _ := os.OpenFile(filepath.Join(hostRWSharedDirectory, "readwrite"), os.O_RDWR|os.O_CREATE, 0755)
fRW.Close()

resources := &ctrdtaskapi.ContainerMount{
HostPath: hostRWSharedDirectory,
ContainerPath: hostRWSharedDirectory,
ReadOnly: true,
Type: "",
}
any, err := typeurl.MarshalAny(resources)
if err != nil {
t.Fatal(err)
}

resp, err := s.updateInternal(context.TODO(), &task.UpdateTaskRequest{ID: t1.ID(), Resources: protobuf.FromAny(any)})
if err != nil {
t.Fatalf("should not have failed update mount with error, got: %v", err)
}
if resp == nil {
t.Fatalf("should have returned an empty resp")
}
}

func Test_TaskShimWindowsMount_updateInternal_Error(t *testing.T) {
s, t1, _ := setupTaskServiceWithFakes(t)
t1.isWCOW = true

hostRWSharedDirectory := t.TempDir()
tmpVhdPath := filepath.Join(hostRWSharedDirectory, "test-vhd.vhdx")

fRW, _ := os.OpenFile(filepath.Join(tmpVhdPath, "readwrite"), os.O_RDWR|os.O_CREATE, 0755)
fRW.Close()

resources := &ctrdtaskapi.ContainerMount{
HostPath: tmpVhdPath,
ContainerPath: tmpVhdPath,
ReadOnly: true,
Type: "virtual-disk",
}
any, err := typeurl.MarshalAny(resources)
if err != nil {
t.Fatal(err)
}

resp, err := s.updateInternal(context.TODO(), &task.UpdateTaskRequest{ID: t1.ID(), Resources: protobuf.FromAny(any)})
if err == nil {
t.Fatalf("should have failed update mount with error, got: %v", err)
}
if resp != nil {
t.Fatalf("should have returned a nil resp")
}
}

func Test_TaskShim_updateInternal_Error(t *testing.T) {
s, t1, _ := setupTaskServiceWithFakes(t)

Expand Down
1 change: 1 addition & 0 deletions cmd/containerd-shim-runhcs-v1/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func verifyTaskUpdateResourcesType(data interface{}) error {
case *specs.WindowsResources:
case *specs.LinuxResources:
case *ctrdtaskapi.PolicyFragment:
case *ctrdtaskapi.ContainerMount:
default:
return errNotSupportedResourcesRequest
}
Expand Down
105 changes: 104 additions & 1 deletion cmd/containerd-shim-runhcs-v1/task_hcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"go.opencensus.io/trace"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/Microsoft/go-winio/pkg/fs"
runhcsopts "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
"github.com/Microsoft/hcsshim/internal/cmd"
Expand All @@ -40,10 +41,12 @@ import (
"github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
"github.com/Microsoft/hcsshim/internal/resources"
"github.com/Microsoft/hcsshim/internal/schemaversion"
"github.com/Microsoft/hcsshim/internal/shimdiag"
"github.com/Microsoft/hcsshim/internal/uvm"
"github.com/Microsoft/hcsshim/osversion"
"github.com/Microsoft/hcsshim/pkg/annotations"
"github.com/Microsoft/hcsshim/pkg/ctrdtaskapi"
)

func newHcsStandaloneTask(ctx context.Context, events publisher, req *task.CreateTaskRequest, s *specs.Spec) (shimTask, error) {
Expand Down Expand Up @@ -862,7 +865,15 @@ func (ht *hcsTask) Update(ctx context.Context, req *task.UpdateTaskRequest) erro

func (ht *hcsTask) updateTaskContainerResources(ctx context.Context, data interface{}, annotations map[string]string) error {
if ht.isWCOW {
return ht.updateWCOWResources(ctx, data, annotations)
switch data.(type) {
case *specs.WindowsResources:
return ht.updateWCOWResources(ctx, data, annotations)
case *ctrdtaskapi.ContainerMount:
// Adding mount to a running container is currently only supported for windows containers
return ht.updateWCOWContainerMount(ctx, data, annotations)
default:
return errNotSupportedResourcesRequest
}
}

return ht.updateLCOWResources(ctx, data, annotations)
Expand Down Expand Up @@ -961,3 +972,95 @@ func (ht *hcsTask) ProcessorInfo(ctx context.Context) (*processorInfo, error) {
count: ht.host.ProcessorCount(),
}, nil
}

// Add mount as vSMB share to the UVM at the given destination path
func (ht *hcsTask) addMountToUVM(ctx context.Context, src string, dst string, isRO bool) (string, error) {
options := ht.host.DefaultVSMBOptions(isRO)
vsmbShare, err := ht.host.AddVSMB(ctx, src, options)
if err != nil {
return "", errors.Wrapf(err, "failed to add mount as vSMB share to UVM")
}
defer func() {
if err != nil {
_ = vsmbShare.Release(ctx)
}
}()

sharePath, err := ht.host.GetVSMBUvmPath(ctx, src, isRO)
if err != nil {
return "", errors.Wrapf(err, "failed to get vsmb path")
}
return sharePath, nil
}

func (ht *hcsTask) requestAddContainerMount(ctx context.Context, resourcePath string, settings interface{}) error {
modification := &hcsschema.ModifySettingRequest{
ResourcePath: resourcePath,
RequestType: guestrequest.RequestTypeAdd,
Settings: settings,
}
return ht.c.Modify(ctx, modification)
}

func isMountTypeSupported(mountType string) bool {
// currently we only support mounting of host volumes/directories
return mountType == ""
}

func (ht *hcsTask) updateWCOWContainerMount(ctx context.Context, data interface{}, annotations map[string]string) error {
// Hcsschema v2 should be supported
if osversion.Build() >= osversion.RS5 {
if sv := schemaversion.DetermineSchemaVersion(nil); !schemaversion.IsV21(sv) {
return fmt.Errorf("hcsschema v2 unsupported")
}
}
resources, ok := data.(*ctrdtaskapi.ContainerMount)
if !ok {
return errors.New("resources must be of type *ctrdtaskapi.ContainerMount when adding mount to a running windows container")
}
if resources.HostPath == "" || resources.ContainerPath == "" {
return fmt.Errorf("invalid OCI spec - a mount must have both host and container path set")
}

// Check for valid mount type
if !isMountTypeSupported(resources.Type) {
return fmt.Errorf("invalid mount type - currently only host volumes/directories can be mounted to running containers")
}

if ht.host == nil {
// HCS has a bug where it does not correctly resolve file (not dir) paths
// if the path includes a symlink. Therefore, we resolve the path here before
// passing it in. The issue does not occur with VSMB, so don't need to worry
// about the isolated case.
hostPath, err := fs.ResolvePath(resources.HostPath)
if err != nil {
return errors.Wrapf(err, "failed to resolve path for hostPath %s", resources.HostPath)
}

// process isolated windows container
settings := hcsschema.MappedDirectory{
HostPath: hostPath,
ContainerPath: resources.ContainerPath,
ReadOnly: resources.ReadOnly,
}
if err := ht.requestAddContainerMount(ctx, resourcepaths.SiloMappedDirectoryResourcePath, settings); err != nil {
return errors.Wrapf(err, "failed to add mount to process isolated container")
}
} else {
// if it is a mount request for a running hyperV WCOW container, we should first mount volume to the
// UVM as a VSMB share and then mount to the running container using the src path as seen by the UVM
guestPath, err := ht.addMountToUVM(ctx, resources.HostPath, resources.ContainerPath, resources.ReadOnly)
if err != nil {
return err
}
settings := hcsschema.MappedDirectory{
HostPath: guestPath,
ContainerPath: resources.ContainerPath,
ReadOnly: resources.ReadOnly,
}
if err := ht.requestAddContainerMount(ctx, resourcepaths.SiloMappedDirectoryResourcePath, settings); err != nil {
return errors.Wrapf(err, "failed to add mount to hyperV container")
}
}
return nil
}
21 changes: 20 additions & 1 deletion cmd/containerd-shim-runhcs-v1/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
"github.com/Microsoft/hcsshim/internal/shimdiag"
"github.com/Microsoft/hcsshim/pkg/ctrdtaskapi"
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
task "github.com/containerd/containerd/api/runtime/task/v2"
"github.com/containerd/containerd/errdefs"
Expand Down Expand Up @@ -107,7 +108,25 @@ func (tst *testShimTask) Update(ctx context.Context, req *task.UpdateTaskRequest
if err != nil {
return errors.Wrapf(err, "failed to unmarshal resources for container %s update request", req.ID)
}
return verifyTaskUpdateResourcesType(data)
if err := verifyTaskUpdateResourcesType(data); err != nil {
return err
}

if tst.isWCOW {
switch data.(type) {
case *ctrdtaskapi.ContainerMount:
mount, _ := data.(*ctrdtaskapi.ContainerMount)
// Adding mount to a running container is currently only supported for windows containers
if isMountTypeSupported(mount.Type) {
return nil
} else {
return errNotSupportedResourcesRequest
}
default:
return errNotSupportedResourcesRequest
}
}
return nil
}

func (tst *testShimTask) Share(ctx context.Context, req *shimdiag.ShareRequest) error {
Expand Down
8 changes: 8 additions & 0 deletions pkg/ctrdtaskapi/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

func init() {
typeurl.Register(&PolicyFragment{}, "github.com/Microsoft/hcsshim/pkg/ctrdtaskapi", "PolicyFragment")
typeurl.Register(&ContainerMount{}, "github.com/Microsoft/hcsshim/pkg/ctrdtaskapi", "ContainerMount")
}

type PolicyFragment struct {
Expand All @@ -15,3 +16,10 @@ type PolicyFragment struct {
// fragment and any additional information required for validation.
Fragment string `json:"fragment,omitempty"`
}

type ContainerMount struct {
HostPath string
ContainerPath string
ReadOnly bool
Type string
}

0 comments on commit 694d751

Please sign in to comment.