devices: add interface "sandbox.AddDevice"

Fixes #50 .

Add new interface sandbox.AddDevice, then for Frakti use case, a device
can be attached to sandbox before container is created.

Signed-off-by: Wei Zhang <zhangwei555@huawei.com>
This commit is contained in:
Wei Zhang 2018-08-05 22:22:38 +08:00
parent dd2acd26eb
commit 6e6be98b15
11 changed files with 182 additions and 6 deletions

View File

@ -11,6 +11,7 @@ import (
"syscall" "syscall"
deviceApi "github.com/kata-containers/runtime/virtcontainers/device/api" deviceApi "github.com/kata-containers/runtime/virtcontainers/device/api"
deviceConfig "github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -760,3 +761,24 @@ func PauseContainer(sandboxID, containerID string) error {
func ResumeContainer(sandboxID, containerID string) error { func ResumeContainer(sandboxID, containerID string) error {
return togglePauseContainer(sandboxID, containerID, false) return togglePauseContainer(sandboxID, containerID, false)
} }
// AddDevice will add a device to sandbox
func AddDevice(sandboxID string, info deviceConfig.DeviceInfo) (deviceApi.Device, error) {
if sandboxID == "" {
return nil, errNeedSandboxID
}
lockFile, err := rwLockSandbox(sandboxID)
if err != nil {
return nil, err
}
defer unlockSandbox(lockFile)
s, err := fetchSandbox(sandboxID)
if err != nil {
return nil, err
}
defer s.releaseStatelessSandbox()
return s.AddDevice(info)
}

View File

@ -1133,13 +1133,22 @@ func (c *Container) attachDevices() error {
func (c *Container) detachDevices() error { func (c *Container) detachDevices() error {
for _, dev := range c.devices { for _, dev := range c.devices {
if err := c.sandbox.devManager.DetachDevice(dev.ID, c.sandbox); err != nil { err := c.sandbox.devManager.DetachDevice(dev.ID, c.sandbox)
if err == manager.ErrDeviceNotAttached { if err != nil && err != manager.ErrDeviceNotAttached {
// skip if device isn't attached
continue
}
return err return err
} }
if err = c.sandbox.devManager.RemoveDevice(dev.ID); err != nil {
c.Logger().WithFields(logrus.Fields{
"container": c.id,
"device-id": dev.ID,
}).WithError(err).Error("remove device failed")
// ignore the device not exist error
if err != manager.ErrDeviceNotExist {
return err
}
}
} }
if err := c.sandbox.storeSandboxDevices(); err != nil { if err := c.sandbox.storeSandboxDevices(); err != nil {

View File

@ -62,6 +62,7 @@ type Device interface {
// device management object. // device management object.
type DeviceManager interface { type DeviceManager interface {
NewDevice(config.DeviceInfo) (Device, error) NewDevice(config.DeviceInfo) (Device, error)
RemoveDevice(string) error
AttachDevice(string, DeviceReceiver) error AttachDevice(string, DeviceReceiver) error
DetachDevice(string, DeviceReceiver) error DetachDevice(string, DeviceReceiver) error
IsDeviceAttached(string) bool IsDeviceAttached(string) bool

View File

@ -93,7 +93,7 @@ func (dm *deviceManager) createDevice(devInfo config.DeviceInfo) (api.Device, er
} }
} }
// NewDevice creates bundles of devices based on array of DeviceInfo // NewDevice creates a device based on specified DeviceInfo
func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error) { func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error) {
dm.Lock() dm.Lock()
defer dm.Unlock() defer dm.Unlock()
@ -104,6 +104,17 @@ func (dm *deviceManager) NewDevice(devInfo config.DeviceInfo) (api.Device, error
return dev, err return dev, err
} }
// RemoveDevice deletes the device from list based on specified device id
func (dm *deviceManager) RemoveDevice(id string) error {
dm.Lock()
defer dm.Unlock()
if _, ok := dm.devices[id]; !ok {
return ErrDeviceNotExist
}
delete(dm.devices, id)
return nil
}
func (dm *deviceManager) newDeviceID() (string, error) { func (dm *deviceManager) newDeviceID() (string, error) {
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
// generate an random ID // generate an random ID

View File

@ -12,6 +12,8 @@ package virtcontainers
import ( import (
"syscall" "syscall"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -141,3 +143,8 @@ func (impl *VCImpl) PauseContainer(sandboxID, containerID string) error {
func (impl *VCImpl) ResumeContainer(sandboxID, containerID string) error { func (impl *VCImpl) ResumeContainer(sandboxID, containerID string) error {
return ResumeContainer(sandboxID, containerID) return ResumeContainer(sandboxID, containerID)
} }
// AddDevice will add a device to sandbox
func (impl *VCImpl) AddDevice(sandboxID string, info config.DeviceInfo) (api.Device, error) {
return AddDevice(sandboxID, info)
}

View File

@ -9,6 +9,8 @@ import (
"io" "io"
"syscall" "syscall"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -41,6 +43,8 @@ type VC interface {
UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error
PauseContainer(sandboxID, containerID string) error PauseContainer(sandboxID, containerID string) error
ResumeContainer(sandboxID, containerID string) error ResumeContainer(sandboxID, containerID string) error
AddDevice(sandboxID string, info config.DeviceInfo) (api.Device, error)
} }
// VCSandbox is the Sandbox interface // VCSandbox is the Sandbox interface
@ -70,6 +74,8 @@ type VCSandbox interface {
SignalProcess(containerID, processID string, signal syscall.Signal, all bool) error SignalProcess(containerID, processID string, signal syscall.Signal, all bool) error
WinsizeProcess(containerID, processID string, height, width uint32) error WinsizeProcess(containerID, processID string, height, width uint32) error
IOStream(containerID, processID string) (io.WriteCloser, io.Reader, io.Reader, error) IOStream(containerID, processID string) (io.WriteCloser, io.Reader, io.Reader, error)
AddDevice(info config.DeviceInfo) (api.Device, error)
} }
// VCContainer is the Container interface // VCContainer is the Container interface

View File

@ -20,6 +20,8 @@ import (
"syscall" "syscall"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -239,3 +241,12 @@ func (m *VCMock) ResumeContainer(sandboxID, containerID string) error {
return fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID) return fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
} }
// AddDevice implements the VC function of the same name.
func (m *VCMock) AddDevice(sandboxID string, info config.DeviceInfo) (api.Device, error) {
if m.AddDeviceFunc != nil {
return m.AddDeviceFunc(sandboxID, info)
}
return nil, fmt.Errorf("%s: %s (%+v): sandboxID: %v", mockErrorPrefix, getSelf(), m, sandboxID)
}

View File

@ -10,6 +10,8 @@ import (
"syscall" "syscall"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
) )
@ -138,3 +140,8 @@ func (s *Sandbox) WinsizeProcess(containerID, processID string, height, width ui
func (s *Sandbox) IOStream(containerID, processID string) (io.WriteCloser, io.Reader, io.Reader, error) { func (s *Sandbox) IOStream(containerID, processID string) (io.WriteCloser, io.Reader, io.Reader, error) {
return nil, nil, nil, nil return nil, nil, nil, nil
} }
// AddDevice adds a device to sandbox
func (s *Sandbox) AddDevice(info config.DeviceInfo) (api.Device, error) {
return nil, nil
}

View File

@ -9,6 +9,8 @@ import (
"syscall" "syscall"
vc "github.com/kata-containers/runtime/virtcontainers" vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/device/api"
"github.com/kata-containers/runtime/virtcontainers/device/config"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -61,4 +63,6 @@ type VCMock struct {
UpdateContainerFunc func(sandboxID, containerID string, resources specs.LinuxResources) error UpdateContainerFunc func(sandboxID, containerID string, resources specs.LinuxResources) error
PauseContainerFunc func(sandboxID, containerID string) error PauseContainerFunc func(sandboxID, containerID string) error
ResumeContainerFunc func(sandboxID, containerID string) error ResumeContainerFunc func(sandboxID, containerID string) error
AddDeviceFunc func(sandboxID string, info config.DeviceInfo) (api.Device, error)
} }

View File

@ -1546,3 +1546,25 @@ func (s *Sandbox) AppendDevice(device api.Device) error {
} }
return fmt.Errorf("unsupported device type") return fmt.Errorf("unsupported device type")
} }
// AddDevice will add a device to sandbox
func (s *Sandbox) AddDevice(info config.DeviceInfo) (api.Device, error) {
if s.devManager == nil {
return nil, fmt.Errorf("device manager isn't initialized")
}
b, err := s.devManager.NewDevice(info)
if err != nil {
return nil, err
}
if err := s.devManager.AttachDevice(b.DeviceID(), s); err != nil {
return nil, err
}
if err := s.storeSandboxDevices(); err != nil {
return nil, err
}
return b, nil
}

View File

@ -1629,3 +1629,79 @@ func TestAttachBlockDevice(t *testing.T) {
err = device.Detach(sandbox) err = device.Detach(sandbox)
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestPreAddDevice(t *testing.T) {
fs := &filesystem{}
hypervisor := &mockHypervisor{}
hConfig := HypervisorConfig{
BlockDeviceDriver: VirtioBlock,
}
sconfig := &SandboxConfig{
HypervisorConfig: hConfig,
}
dm := manager.NewDeviceManager(VirtioBlock, nil)
// create a sandbox first
sandbox := &Sandbox{
id: testSandboxID,
storage: fs,
hypervisor: hypervisor,
config: sconfig,
devManager: dm,
}
contID := "100"
container := Container{
sandbox: sandbox,
id: contID,
sandboxID: testSandboxID,
}
container.state.State = StateReady
// create state file
path := filepath.Join(runStoragePath, testSandboxID, container.ID())
err := os.MkdirAll(path, dirMode)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(path)
stateFilePath := filepath.Join(path, stateFile)
os.Remove(stateFilePath)
_, err = os.Create(stateFilePath)
if err != nil {
t.Fatal(err)
}
defer os.Remove(stateFilePath)
path = "/dev/hda"
deviceInfo := config.DeviceInfo{
HostPath: path,
ContainerPath: path,
DevType: "b",
}
// Add a mount device for a mountpoint before container's creation
dev, err := sandbox.AddDevice(deviceInfo)
assert.Nil(t, err)
// in Frakti use case, here we will create and start the container
// which will attach same device twice
container.mounts = []Mount{
{
Destination: path,
Source: path,
Type: "bind",
BlockDeviceID: dev.DeviceID(),
},
}
mounts, err := container.mountSharedDirMounts("", "")
assert.Nil(t, err)
assert.Equal(t, len(mounts), 0,
"mounts should contain nothing because it only contains a block device")
}