Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ jobs:
- name: Test repo
run: ${{ env.GOTESTSUM_CMD }} -gcflags=all=-d=checkptr -tags admin -timeout=20m ./...

- name: Test lcow tagged packages (shimV2)
run: ${{ env.GOTESTSUM_CMD }} -gcflags=all=-d=checkptr -tags lcow -timeout=10m ./...

- name: Test wcow tagged packages (shimV2)
run: ${{ env.GOTESTSUM_CMD }} -gcflags=all=-d=checkptr -tags wcow -timeout=10m ./...

- name: Run non-functional tests
run: ${{ env.GOTESTSUM_CMD }} -mod=mod -gcflags=all=-d=checkptr ./internal/... ./pkg/... ./parity/...
working-directory: test
Expand Down Expand Up @@ -637,8 +643,16 @@ jobs:
$LASTEXITCODE = 0
}

- run: ${{ env.GO_BUILD_CMD }} -tags lcow ./...
name: Build lcow tagged packages (shimV2)
- run: ${{ env.GO_BUILD_CMD }} -tags wcow ./...
name: Build wcow tagged packages (shimV2)
- run: ${{ env.GO_BUILD_CMD }} ./...
name: Build untagged packages
- run: ${{ env.GO_BUILD_CMD }} ./cmd/containerd-shim-runhcs-v1
name: Build containerd-shim-runhcs-v1.exe
- run: ${{ env.GO_BUILD_CMD }} -tags lcow ./cmd/containerd-shim-lcow-v2
name: Build containerd-shim-lcow-v2.exe
- run: ${{ env.GO_BUILD_CMD }} ./cmd/device-util
name: Build device-util.exe
- run: ${{ env.GO_BUILD_CMD }} ./cmd/jobobject-util
Expand Down
20 changes: 8 additions & 12 deletions internal/controller/device/scsi/controller.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build windows
//go:build windows && (lcow || wcow)

package scsi

Expand Down Expand Up @@ -39,12 +39,9 @@ type Controller struct {
// vm is the host-side interface for adding and removing SCSI disks.
// Immutable after construction.
vm VMSCSIOps
// linuxGuest is the guest-side interface for LCOW SCSI operations.
// guest is the guest-side interface for SCSI operations.
// Immutable after construction.
linuxGuest LinuxGuestSCSIOps
// windowsGuest is the guest-side interface for WCOW SCSI operations.
// Immutable after construction.
windowsGuest WindowsGuestSCSIOps
guest GuestSCSIOps

// reservations maps a reservation ID to its disk slot and partition.
// Guarded by mu.
Expand All @@ -65,11 +62,10 @@ type Controller struct {

// New creates a new [Controller] for the given number of SCSI controllers and
// host/guest operation interfaces.
func New(numControllers int, vm VMSCSIOps, linuxGuest LinuxGuestSCSIOps, windowsGuest WindowsGuestSCSIOps) *Controller {
func New(numControllers int, vm VMSCSIOps, guest GuestSCSIOps) *Controller {
return &Controller{
vm: vm,
linuxGuest: linuxGuest,
windowsGuest: windowsGuest,
guest: guest,
reservations: make(map[guid.GUID]*reservation),
disksByPath: make(map[string]int),
controllerSlots: make([]*disk.Disk, numControllers*numLUNsPerController),
Expand Down Expand Up @@ -200,7 +196,7 @@ func (c *Controller) MapToGuest(ctx context.Context, id guid.GUID) (string, erro
}

// Mount the partition inside the guest.
guestPath, err := existingDisk.MountPartitionToGuest(ctx, r.partition, c.linuxGuest, c.windowsGuest)
guestPath, err := existingDisk.MountPartitionToGuest(ctx, r.partition, c.guest)
if err != nil {
return "", fmt.Errorf("mount partition %d to guest: %w", r.partition, err)
}
Expand Down Expand Up @@ -232,12 +228,12 @@ func (c *Controller) UnmapFromGuest(ctx context.Context, id guid.GUID) error {

// Unmount the partition from the guest (ref-counted; only issues the
// guest call when this is the last reservation on the partition).
if err := existingDisk.UnmountPartitionFromGuest(ctx, r.partition, c.linuxGuest, c.windowsGuest); err != nil {
if err := existingDisk.UnmountPartitionFromGuest(ctx, r.partition, c.guest); err != nil {
return fmt.Errorf("unmount partition %d from guest: %w", r.partition, err)
}

// Detach the disk from the VM when no partitions remain active.
if err := existingDisk.DetachFromVM(ctx, c.vm, c.linuxGuest); err != nil {
if err := existingDisk.DetachFromVM(ctx, c.vm, c.guest); err != nil {
return fmt.Errorf("detach disk from VM: %w", err)
}

Expand Down
73 changes: 73 additions & 0 deletions internal/controller/device/scsi/controller_lcow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//go:build windows && lcow

package scsi

import (
"context"
"errors"
"testing"

"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
)

// --- Mock type ---

type mockGuestOps struct {
mountErr error
unmountErr error
ejectErr error
}

func (m *mockGuestOps) AddLCOWMappedVirtualDisk(_ context.Context, _ guestresource.LCOWMappedVirtualDisk) error {
return m.mountErr
}

func (m *mockGuestOps) RemoveLCOWMappedVirtualDisk(_ context.Context, _ guestresource.LCOWMappedVirtualDisk) error {
return m.unmountErr
}

func (m *mockGuestOps) RemoveSCSIDevice(_ context.Context, _ guestresource.SCSIDevice) error {
return m.ejectErr
}

// --- Helpers used by shared tests ---

func newMockGuestOps() *mockGuestOps {
return &mockGuestOps{}
}

// --- LCOW-specific tests ---

func TestMapToGuest_MountError(t *testing.T) {
guest := &mockGuestOps{mountErr: errors.New("mount failed")}
c := New(1, &mockVMOps{}, guest)
id, err := c.Reserve(context.Background(), defaultDiskConfig(), defaultMountConfig())
if err != nil {
t.Fatalf("Reserve: %v", err)
}
_, err = c.MapToGuest(context.Background(), id)
if err == nil {
t.Fatal("expected error, got nil")
}
}

func TestUnmapFromGuest_UnmountError(t *testing.T) {
guest := &mockGuestOps{}
c := New(1, &mockVMOps{}, guest)
dc := defaultDiskConfig()
mc := defaultMountConfig()

id, err := c.Reserve(context.Background(), dc, mc)
if err != nil {
t.Fatalf("Reserve: %v", err)
}
if _, err := c.MapToGuest(context.Background(), id); err != nil {
t.Fatalf("MapToGuest: %v", err)
}
// Now inject an unmount error.
guest.unmountErr = errors.New("unmount failed")
err = c.UnmapFromGuest(context.Background(), id)
if err == nil {
t.Fatal("expected error, got nil")
}
}
Loading
Loading