Skip to content

Commit

Permalink
Merge pull request kata-containers#299 from devimc/topic/vsocks
Browse files Browse the repository at this point in the history
channel: support communication channel hotplug
  • Loading branch information
bergwolf authored Aug 10, 2018
2 parents fcfa054 + 4f70b1c commit 9140c9c
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 76 deletions.
2 changes: 1 addition & 1 deletion api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

// Serial channel
const (
var (
serialChannelName = "agent.channel.0"
virtIOPath = "/sys/class/virtio-ports"
devRootPath = "/dev"
Expand Down
85 changes: 42 additions & 43 deletions channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/hashicorp/yamux"
"github.com/mdlayher/vsock"
Expand All @@ -21,35 +22,58 @@ import (
grpcStatus "google.golang.org/grpc/status"
)

var (
channelExistMaxTries = 200
channelExistWaitTime = 50 * time.Millisecond
isAFVSockSupportedFunc = isAFVSockSupported
)

type channel interface {
setup() error
wait() error
listen() (net.Listener, error)
teardown() error
}

// Creates a new channel to communicate the agent with the proxy or shim.
// The runtime hot plugs a serial port or a vsock PCI depending of the configuration
// file and if the host has support for vsocks. newChannel iterates in a loop looking
// for the serial port or vsock device.
// The timeout is defined by channelExistMaxTries and channelExistWaitTime and it
// can be calculated by using the following operation:
// (channelExistMaxTries * channelExistWaitTime) / 1000 = timeout in seconds
// If there are neither vsocks nor serial ports, an error is returned.
func newChannel() (channel, error) {
// Check for vsock support.
vSockSupported, err := isAFVSockSupported()
if err != nil {
return nil, err
}

if vSockSupported {
// Check if vsock socket exists. We want to cover the case
// where the guest OS can support vsock, but the runtime is
// still using virtio serial connection.
exist, err := vSockPathExist()
if err != nil {
return nil, err
var serialErr error
var serialPath string
var vsockErr error
var vSockSupported bool

for i := 0; i < channelExistMaxTries; i++ {
// check vsock path
if _, err := os.Stat(vSockDevPath); err == nil {
if vSockSupported, vsockErr = isAFVSockSupportedFunc(); vSockSupported && vsockErr == nil {
return &vSockChannel{}, nil
}
}

if exist {
return &vSockChannel{}, nil
// Check serial port path
if serialPath, serialErr = findVirtualSerialPath(serialChannelName); serialErr == nil {
return &serialChannel{serialPath: serialPath}, nil
}

time.Sleep(channelExistWaitTime)
}

if serialErr != nil {
agentLog.WithError(serialErr).Error("Serial port not found")
}

if vsockErr != nil {
agentLog.WithError(vsockErr).Error("VSock not found")
}

return &serialChannel{}, nil
return nil, fmt.Errorf("Neither vsocks nor serial ports were found")
}

type vSockChannel struct {
Expand Down Expand Up @@ -77,12 +101,13 @@ func (c *vSockChannel) teardown() error {
}

type serialChannel struct {
serialPath string
serialConn *os.File
}

func (c *serialChannel) setup() error {
// Open serial channel.
file, err := openSerialChannel()
file, err := os.OpenFile(c.serialPath, os.O_RDWR, os.ModeDevice)
if err != nil {
return err
}
Expand Down Expand Up @@ -202,18 +227,6 @@ func isAFVSockSupported() (bool, error) {
return true, nil
}

func vSockPathExist() (bool, error) {
if _, err := os.Stat(vSockDevPath); err != nil {
if os.IsNotExist(err) {
return false, nil
}

return false, err
}

return true, nil
}

func findVirtualSerialPath(serialName string) (string, error) {
dir, err := os.Open(virtIOPath)
if err != nil {
Expand Down Expand Up @@ -245,17 +258,3 @@ func findVirtualSerialPath(serialName string) (string, error) {

return "", grpcStatus.Errorf(codes.NotFound, "Could not find virtio port %s", serialName)
}

func openSerialChannel() (*os.File, error) {
serialPath, err := findVirtualSerialPath(serialChannelName)
if err != nil {
return nil, err
}

file, err := os.OpenFile(serialPath, os.O_RDWR, os.ModeDevice)
if err != nil {
return nil, err
}

return file, nil
}
85 changes: 53 additions & 32 deletions channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,15 @@
package main

import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestVSockPathExistTrue(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "test")
assert.Nil(t, err, "%v", err)
fileName := tmpFile.Name()
defer tmpFile.Close()
defer os.Remove(fileName)

vSockDevPath = fileName

result, err := vSockPathExist()
assert.Nil(t, err, "%v", err)

assert.True(t, result, "VSOCK should be found")
}

func TestVSockPathExistFalse(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "test")
assert.Nil(t, err, "%v", err)

fileName := tmpFile.Name()
tmpFile.Close()
err = os.Remove(fileName)
assert.Nil(t, err, "%v", err)

vSockDevPath = fileName

result, err := vSockPathExist()
assert.Nil(t, err, "%v", err)

assert.False(t, result, "VSOCK should not be found")
}

func TestSetupVSockChannel(t *testing.T) {
c := &vSockChannel{}

Expand Down Expand Up @@ -97,3 +67,54 @@ func TestTeardownSerialChannel(t *testing.T) {
err = c.teardown()
assert.Nil(t, err, "%v", err)
}

func TestNewChannel(t *testing.T) {
assert := assert.New(t)

orgChannelExistMaxTries := channelExistMaxTries
orgChannelExistWaitTime := channelExistWaitTime
orgVSockDevPath := vSockDevPath
orgVirtIOPath := virtIOPath
orgIsAFVSockSupportedFunc := isAFVSockSupportedFunc
channelExistMaxTries = 1
channelExistWaitTime = 0
vSockDevPath = "/abc/xyz/123"
virtIOPath = "/abc/xyz/123"
isAFVSockSupportedFunc = func() (bool, error) { return false, errors.New("vsock") }
defer func() {
channelExistMaxTries = orgChannelExistMaxTries
channelExistWaitTime = orgChannelExistWaitTime
vSockDevPath = orgVSockDevPath
virtIOPath = orgVirtIOPath
isAFVSockSupportedFunc = orgIsAFVSockSupportedFunc
}()

c, err := newChannel()
assert.Error(err)
assert.Nil(c)

vSockDevPath = "/dev/null"
c, err = newChannel()
assert.Error(err)
assert.Nil(c)

isAFVSockSupportedFunc = func() (bool, error) { return true, nil }
c, err = newChannel()
assert.NoError(err)
_, ok := c.(*vSockChannel)
assert.True(ok)

vSockDevPath = "/abc/xyz/123"
virtIOPath, err = ioutil.TempDir("", "virtio")
assert.NoError(err)
portPath := filepath.Join(virtIOPath, "port")
err = os.Mkdir(portPath, 0777)
assert.NoError(err)
defer os.Remove(portPath)
err = ioutil.WriteFile(filepath.Join(portPath, "name"), []byte(serialChannelName), 0777)
assert.NoError(err)
c, err = newChannel()
assert.NoError(err)
_, ok = c.(*serialChannel)
assert.True(ok)
}

0 comments on commit 9140c9c

Please sign in to comment.