Skip to content
This repository was archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
virtcontainers: Add support for ephemeral volumes
Browse files Browse the repository at this point in the history
Ephemeral volumes should not be passed at 9pfs mounts.
They should be created inside the VM.

This patch disables ephemeral volumes from getting
mounted as 9pfs from the host and instead a corresponding
tmpfs is created inside the VM.

Fixes : #61

Signed-off-by: Harshal Patil <harshal.patil@in.ibm.com>
  • Loading branch information
Harshal Patil committed Jul 17, 2018
1 parent 6f409b9 commit eb51da8
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 2 deletions.
17 changes: 16 additions & 1 deletion cli/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool,
disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)

var process vc.Process

switch containerType {
case vc.PodSandbox:
process, err = createSandbox(ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput)
Expand Down Expand Up @@ -246,9 +245,25 @@ func createSandbox(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
return containers[0].Process(), nil
}

// setEphemeralStorageType sets the mount type to 'ephemeral'
// if the mount source path is provisioned by k8s for ephemeral storage.
// For the given pod ephemeral volume is created only once
// backed by tmpfs inside the VM. For successive containers
// of the same pod the already existing volume is reused.
func setEphemeralStorageType(ociSpec oci.CompatOCISpec) oci.CompatOCISpec {
for idx, mnt := range ociSpec.Mounts {
if IsEphemeralStorage(mnt.Source) {
ociSpec.Mounts[idx].Type = "ephemeral"
}
}
return ociSpec
}

func createContainer(ociSpec oci.CompatOCISpec, containerID, bundlePath,
console string, disableOutput bool) (vc.Process, error) {

ociSpec = setEphemeralStorageType(ociSpec)

contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
if err != nil {
return vc.Process{}, err
Expand Down
18 changes: 18 additions & 0 deletions cli/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,24 @@ func TestCreateCreateContainerFail(t *testing.T) {
}
}

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

ociSpec := oci.CompatOCISpec{}
var ociMounts []specs.Mount
mount := specs.Mount{
Source: "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume",
}

ociMounts = append(ociMounts, mount)
ociSpec.Mounts = ociMounts
ociSpec = setEphemeralStorageType(ociSpec)

mountType := ociSpec.Mounts[0].Type
assert.Equal(mountType, "ephemeral",
"Unexpected mount type, got %s expected ephemeral", mountType)
}

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

Expand Down
25 changes: 24 additions & 1 deletion cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import (
"strings"
)

const unknown = "<<unknown>>"
const (
unknown = "<<unknown>>"
k8sEmptyDir = "kubernetes.io~empty-dir"
)

// variables to allow tests to modify the values
var (
Expand Down Expand Up @@ -43,6 +46,26 @@ func getFileContents(file string) (string, error) {
return string(bytes), nil
}

// IsEphemeralStorage returns true if the given path
// to the storage belongs to kubernetes ephemeral storage
//
// This method depends on a specific path used by k8s
// to detect if it's of type ephemeral. As of now,
// this is a very k8s specific solution that works
// but in future there should be a better way for this
// method to determine if the path is for ephemeral
// volume type
func IsEphemeralStorage(path string) bool {
splitSourceSlice := strings.Split(path, "/")
if len(splitSourceSlice) > 1 {
storageType := splitSourceSlice[len(splitSourceSlice)-2]
if storageType == k8sEmptyDir {
return true
}
}
return false
}

func getKernelVersion() (string, error) {
contents, err := getFileContents(procVersion)
if err != nil {
Expand Down
14 changes: 14 additions & 0 deletions cli/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ func TestFileExists(t *testing.T) {
fmt.Sprintf("File %q should exist", file))
}

func TestIsEphemeralStorage(t *testing.T) {
sampleEphePath := "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/kubernetes.io~empty-dir/cache-volume"
isEphe := IsEphemeralStorage(sampleEphePath)
if !isEphe {
t.Fatalf("Unable to correctly determine volume type")
}

sampleEphePath = "/var/lib/kubelet/pods/366c3a75-4869-11e8-b479-507b9ddd5ce4/volumes/cache-volume"
isEphe = IsEphemeralStorage(sampleEphePath)
if isEphe {
t.Fatalf("Unable to correctly determine volume type")
}
}

func TestGetFileContents(t *testing.T) {
type testData struct {
contents string
Expand Down
27 changes: 27 additions & 0 deletions virtcontainers/kata_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L", "nodev"}
shmDir = "shm"
kataEphemeralDevType = "ephemeral"
ephemeralPath = filepath.Join(kataGuestSandboxDir, kataEphemeralDevType)
)

// KataAgentConfig is a structure storing information needed
Expand Down Expand Up @@ -781,6 +782,9 @@ func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process,
return nil, err
}

epheStorages := k.handleEphemeralStorage(ociSpec.Mounts)
ctrStorages = append(ctrStorages, epheStorages...)

// We replace all OCI mount sources that match our container mount
// with the right source path (The guest one).
if err = k.replaceOCIMountSource(ociSpec, newMounts); err != nil {
Expand Down Expand Up @@ -846,6 +850,29 @@ func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process,
k.state.URL, c.config.Cmd, createNSList, enterNSList)
}

// handleEphemeralStorage handles ephemeral storages by
// creating a Storage from corresponding source of the mount point
func (k *kataAgent) handleEphemeralStorage(mounts []specs.Mount) []*grpc.Storage {
var epheStorages []*grpc.Storage
for idx, mnt := range mounts {
if mnt.Type == kataEphemeralDevType {
// Set the mount source path to a path that resides inside the VM
mounts[idx].Source = filepath.Join(ephemeralPath, filepath.Base(mnt.Source))

// Create a storage struct so that kata agent is able to create
// tmpfs backed volume inside the VM
epheStorage := &grpc.Storage{
Driver: kataEphemeralDevType,
Source: "tmpfs",
Fstype: "tmpfs",
MountPoint: mounts[idx].Source,
}
epheStorages = append(epheStorages, epheStorage)
}
}
return epheStorages
}

// handleBlockVolumes handles volumes that are block devices files
// by passing the block devices as Storage to the agent.
func (k *kataAgent) handleBlockVolumes(c *Container) []*grpc.Storage {
Expand Down
19 changes: 19 additions & 0 deletions virtcontainers/kata_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,25 @@ func TestGenerateInterfacesAndRoutes(t *testing.T) {

}

func TestHandleEphemeralStorage(t *testing.T) {
k := kataAgent{}
var ociMounts []specs.Mount
mountSource := "/tmp/mountPoint"

mount := specs.Mount{
Type: kataEphemeralDevType,
Source: mountSource,
}

ociMounts = append(ociMounts, mount)
epheStorages := k.handleEphemeralStorage(ociMounts)

epheMountPoint := epheStorages[0].GetMountPoint()
expected := filepath.Join(ephemeralPath, filepath.Base(mountSource))
assert.Equal(t, epheMountPoint, expected,
"Ephemeral mount point didn't match: got %s, expecting %s", epheMountPoint, expected)
}

func TestAppendDevicesEmptyContainerDeviceList(t *testing.T) {
k := kataAgent{}

Expand Down

0 comments on commit eb51da8

Please sign in to comment.