mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-29 12:14:48 +00:00
runtime: persist direct volume mount info
In the direct assigned volume scenario, Kata Containers persists the information required for managing the volume inside the guest on host filesystem. Fixes: #3454 Signed-off-by: Feng Wang <feng.wang@databricks.com>
This commit is contained in:
parent
fa326b4e0f
commit
6e0090abb5
108
src/runtime/pkg/direct-volume/utils.go
Normal file
108
src/runtime/pkg/direct-volume/utils.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright (c) 2022 Databricks Inc.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mountInfoFileName = "mountInfo.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var kataDirectVolumeRootPath = "/run/kata-containers/shared/direct-volumes"
|
||||||
|
|
||||||
|
// MountInfo contains the information needed by Kata to consume a host block device and mount it as a filesystem inside the guest VM.
|
||||||
|
type MountInfo struct {
|
||||||
|
// The type of the volume (ie. block)
|
||||||
|
VolumeType string `json:"volume-type"`
|
||||||
|
// The device backing the volume.
|
||||||
|
Device string `json:"device"`
|
||||||
|
// The filesystem type to be mounted on the volume.
|
||||||
|
FsType string `json:"fstype"`
|
||||||
|
// Additional metadata to pass to the agent regarding this volume.
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
// Additional mount options.
|
||||||
|
Options []string `json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add writes the mount info of a direct volume into a filesystem path known to Kata Container.
|
||||||
|
func Add(volumePath string, mountInfo string) error {
|
||||||
|
volumeDir := filepath.Join(kataDirectVolumeRootPath, volumePath)
|
||||||
|
stat, err := os.Stat(volumeDir)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if stat != nil && !stat.IsDir() {
|
||||||
|
return fmt.Errorf("%s should be a directory", volumeDir)
|
||||||
|
}
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
if err := os.MkdirAll(volumeDir, 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var deserialized MountInfo
|
||||||
|
if err := json.Unmarshal([]byte(mountInfo), &deserialized); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(filepath.Join(volumeDir, mountInfoFileName), []byte(mountInfo), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove deletes the direct volume path including all the files inside it.
|
||||||
|
func Remove(volumePath string) error {
|
||||||
|
// Find the base of the volume path to delete the whole volume path
|
||||||
|
base := strings.SplitN(volumePath, string(os.PathSeparator), 2)[0]
|
||||||
|
return os.RemoveAll(filepath.Join(kataDirectVolumeRootPath, base))
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeMountInfo retrieves the mount info of a direct volume.
|
||||||
|
func VolumeMountInfo(volumePath string) (*MountInfo, error) {
|
||||||
|
mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, volumePath, mountInfoFileName)
|
||||||
|
if _, err := os.Stat(mountInfoFilePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf, err := ioutil.ReadFile(mountInfoFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var mountInfo MountInfo
|
||||||
|
if err := json.Unmarshal(buf, &mountInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mountInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordSandboxId associates a sandbox id with a direct volume.
|
||||||
|
func RecordSandboxId(sandboxId string, volumePath string) error {
|
||||||
|
mountInfoFilePath := filepath.Join(kataDirectVolumeRootPath, volumePath, mountInfoFileName)
|
||||||
|
if _, err := os.Stat(mountInfoFilePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(filepath.Join(kataDirectVolumeRootPath, volumePath, sandboxId), []byte(""), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSandboxIdForVolume(volumePath string) (string, error) {
|
||||||
|
files, err := ioutil.ReadDir(filepath.Join(kataDirectVolumeRootPath, volumePath))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Find the id of the first sandbox.
|
||||||
|
// We expect a direct-assigned volume is associated with only a sandbox at a time.
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name() != mountInfoFileName {
|
||||||
|
return file.Name(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no sandbox found for %s", volumePath)
|
||||||
|
}
|
94
src/runtime/pkg/direct-volume/utils_test.go
Normal file
94
src/runtime/pkg/direct-volume/utils_test.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) 2022 Databricks Inc.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package volume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kata-containers/kata-containers/src/runtime/pkg/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAdd(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
kataDirectVolumeRootPath, err = os.MkdirTemp(os.TempDir(), "add-test")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.RemoveAll(kataDirectVolumeRootPath)
|
||||||
|
var volumePath = "/a/b/c"
|
||||||
|
var basePath = "a"
|
||||||
|
actual := MountInfo{
|
||||||
|
VolumeType: "block",
|
||||||
|
Device: "/dev/sda",
|
||||||
|
FsType: "ext4",
|
||||||
|
Options: []string{"journal_dev", "noload"},
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(actual)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Add the mount info
|
||||||
|
assert.Nil(t, Add(volumePath, string(buf)))
|
||||||
|
|
||||||
|
// Validate the mount info
|
||||||
|
expected, err := VolumeMountInfo(volumePath)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, expected.Device, actual.Device)
|
||||||
|
assert.Equal(t, expected.FsType, actual.FsType)
|
||||||
|
assert.Equal(t, expected.Options, actual.Options)
|
||||||
|
|
||||||
|
// Remove the file
|
||||||
|
err = Remove(volumePath)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
_, err = os.Stat(filepath.Join(kataDirectVolumeRootPath, basePath))
|
||||||
|
assert.True(t, errors.Is(err, os.ErrNotExist))
|
||||||
|
|
||||||
|
// Test invalid mount info json
|
||||||
|
assert.Error(t, Add(volumePath, "{invalid json}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecordSandboxId(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
kataDirectVolumeRootPath, err = os.MkdirTemp(os.TempDir(), "recordSanboxId-test")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.RemoveAll(kataDirectVolumeRootPath)
|
||||||
|
|
||||||
|
var volumePath = "/a/b/c"
|
||||||
|
mntInfo := MountInfo{
|
||||||
|
VolumeType: "block",
|
||||||
|
Device: "/dev/sda",
|
||||||
|
FsType: "ext4",
|
||||||
|
Options: []string{"journal_dev", "noload"},
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(mntInfo)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Add the mount info
|
||||||
|
assert.Nil(t, Add(volumePath, string(buf)))
|
||||||
|
|
||||||
|
sandboxId := uuid.Generate().String()
|
||||||
|
err = RecordSandboxId(sandboxId, volumePath)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
id, err := GetSandboxIdForVolume(volumePath)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, sandboxId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecordSandboxIdNoMountInfoFile(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
kataDirectVolumeRootPath, err = os.MkdirTemp(os.TempDir(), "recordSanboxId-test")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.RemoveAll(kataDirectVolumeRootPath)
|
||||||
|
|
||||||
|
var volumePath = "/a/b/c"
|
||||||
|
sandboxId := uuid.Generate().String()
|
||||||
|
err = RecordSandboxId(sandboxId, volumePath)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.True(t, errors.Is(err, os.ErrNotExist))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user