mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-04 11:06:21 +00:00
virtcontainers: Add support for ephemeral volumes
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>
This commit is contained in:
parent
6f409b9528
commit
b821a5df4c
@ -103,7 +103,6 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool,
|
|||||||
disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)
|
disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)
|
||||||
|
|
||||||
var process vc.Process
|
var process vc.Process
|
||||||
|
|
||||||
switch containerType {
|
switch containerType {
|
||||||
case vc.PodSandbox:
|
case vc.PodSandbox:
|
||||||
process, err = createSandbox(ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput)
|
process, err = createSandbox(ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput)
|
||||||
@ -246,9 +245,25 @@ func createSandbox(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
|
|||||||
return containers[0].Process(), nil
|
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,
|
func createContainer(ociSpec oci.CompatOCISpec, containerID, bundlePath,
|
||||||
console string, disableOutput bool) (vc.Process, error) {
|
console string, disableOutput bool) (vc.Process, error) {
|
||||||
|
|
||||||
|
ociSpec = setEphemeralStorageType(ociSpec)
|
||||||
|
|
||||||
contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
|
contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vc.Process{}, err
|
return vc.Process{}, err
|
||||||
|
@ -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/366c3a77-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) {
|
func TestCreateCreateContainer(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
25
cli/utils.go
25
cli/utils.go
@ -15,7 +15,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const unknown = "<<unknown>>"
|
const (
|
||||||
|
unknown = "<<unknown>>"
|
||||||
|
k8sEmptyDir = "kubernetes.io~empty-dir"
|
||||||
|
)
|
||||||
|
|
||||||
// variables to allow tests to modify the values
|
// variables to allow tests to modify the values
|
||||||
var (
|
var (
|
||||||
@ -43,6 +46,26 @@ func getFileContents(file string) (string, error) {
|
|||||||
return string(bytes), nil
|
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) {
|
func getKernelVersion() (string, error) {
|
||||||
contents, err := getFileContents(procVersion)
|
contents, err := getFileContents(procVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -38,6 +38,20 @@ func TestFileExists(t *testing.T) {
|
|||||||
fmt.Sprintf("File %q should exist", file))
|
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) {
|
func TestGetFileContents(t *testing.T) {
|
||||||
type testData struct {
|
type testData struct {
|
||||||
contents string
|
contents string
|
||||||
|
@ -51,6 +51,7 @@ var (
|
|||||||
sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L", "nodev"}
|
sharedDir9pOptions = []string{"trans=virtio,version=9p2000.L", "nodev"}
|
||||||
shmDir = "shm"
|
shmDir = "shm"
|
||||||
kataEphemeralDevType = "ephemeral"
|
kataEphemeralDevType = "ephemeral"
|
||||||
|
ephemeralPath = filepath.Join(kataGuestSandboxDir, kataEphemeralDevType)
|
||||||
)
|
)
|
||||||
|
|
||||||
// KataAgentConfig is a structure storing information needed
|
// KataAgentConfig is a structure storing information needed
|
||||||
@ -781,6 +782,9 @@ func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epheStorages := k.handleEphemeralStorage(ociSpec.Mounts)
|
||||||
|
ctrStorages = append(ctrStorages, epheStorages...)
|
||||||
|
|
||||||
// We replace all OCI mount sources that match our container mount
|
// We replace all OCI mount sources that match our container mount
|
||||||
// with the right source path (The guest one).
|
// with the right source path (The guest one).
|
||||||
if err = k.replaceOCIMountSource(ociSpec, newMounts); err != nil {
|
if err = k.replaceOCIMountSource(ociSpec, newMounts); err != nil {
|
||||||
@ -846,6 +850,29 @@ func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process,
|
|||||||
k.state.URL, c.config.Cmd, createNSList, enterNSList)
|
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
|
// handleBlockVolumes handles volumes that are block devices files
|
||||||
// by passing the block devices as Storage to the agent.
|
// by passing the block devices as Storage to the agent.
|
||||||
func (k *kataAgent) handleBlockVolumes(c *Container) []*grpc.Storage {
|
func (k *kataAgent) handleBlockVolumes(c *Container) []*grpc.Storage {
|
||||||
|
@ -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) {
|
func TestAppendDevicesEmptyContainerDeviceList(t *testing.T) {
|
||||||
k := kataAgent{}
|
k := kataAgent{}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user