runtime: Add KataVirtualVolume struct in runtime

Add the corresponding data structure in the runtime part according to
kata-containers/kata-containers/pull/7698.

Signed-off-by: ChengyuZhu6 <chengyu.zhu@intel.com>
This commit is contained in:
ChengyuZhu6 2023-08-25 10:30:56 +08:00
parent 51c665a09c
commit d788d4af2f
2 changed files with 402 additions and 0 deletions

View File

@ -0,0 +1,156 @@
package types
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
)
const (
minBlockSize = 1 << 9
maxBlockSize = 1 << 19
)
const (
KataVirtualVolumeDirectBlockType = "direct_block"
KataVirtualVolumeImageRawBlockType = "image_raw_block"
KataVirtualVolumeLayerRawBlockType = "layer_raw_block"
KataVirtualVolumeImageNydusBlockType = "image_nydus_block"
KataVirtualVolumeLayerNydusBlockType = "layer_nydus_block"
KataVirtualVolumeImageNydusFsType = "image_nydus_fs"
KataVirtualVolumeLayerNydusFsType = "layer_nydus_fs"
KataVirtualVolumeImageGuestPullType = "image_guest_pull"
)
// DmVerityInfo contains configuration information for DmVerity device.
type DmVerityInfo struct {
HashType string `json:"hashtype"`
Hash string `json:"hash"`
BlockNum uint64 `json:"blocknum"`
Blocksize uint64 `json:"blocksize"`
Hashsize uint64 `json:"hashsize"`
Offset uint64 `json:"offset"`
}
// DirectAssignedVolume contains meta information for a directly assigned volume.
type DirectAssignedVolume struct {
Metadata map[string]string `json:"metadata"`
}
// ImagePullVolume contains meta information for pulling an image inside the guest.
type ImagePullVolume struct {
Metadata map[string]string `json:"metadata"`
}
// NydusImageVolume contains Nydus image volume information.
type NydusImageVolume struct {
Config string `json:"config"`
SnapshotDir string `json:"snapshot_dir"`
}
// KataVirtualVolume encapsulates information for extra mount options and direct volumes.
type KataVirtualVolume struct {
VolumeType string `json:"volume_type"`
Source string `json:"source,omitempty"`
FSType string `json:"fs_type,omitempty"`
Options []string `json:"options,omitempty"`
DirectVolume *DirectAssignedVolume `json:"direct_volume,omitempty"`
ImagePull *ImagePullVolume `json:"image_pull,omitempty"`
NydusImage *NydusImageVolume `json:"nydus_image,omitempty"`
DmVerity *DmVerityInfo `json:"dm_verity,omitempty"`
}
func (d *DmVerityInfo) IsValid() error {
err := d.validateHashType()
if err != nil {
return err
}
if d.BlockNum == 0 || d.BlockNum > uint64(^uint32(0)) {
return fmt.Errorf("Zero block count for DmVerity device %s", d.Hash)
}
if !isValidBlockSize(d.Blocksize) || !isValidBlockSize(d.Hashsize) {
return fmt.Errorf("Unsupported verity block size: data_block_size = %d, hash_block_size = %d", d.Blocksize, d.Hashsize)
}
if d.Offset%d.Hashsize != 0 || d.Offset < d.Blocksize*d.BlockNum {
return fmt.Errorf("Invalid hashvalue offset %d for DmVerity device %s", d.Offset, d.Hash)
}
return nil
}
func (d *DirectAssignedVolume) IsValid() bool {
return d.Metadata != nil
}
func (i *ImagePullVolume) IsValid() bool {
return i.Metadata != nil
}
func (n *NydusImageVolume) IsValid() bool {
return len(n.Config) > 0 || len(n.SnapshotDir) > 0
}
func (k *KataVirtualVolume) IsValid() bool {
return len(k.VolumeType) > 0 &&
(k.DirectVolume == nil || k.DirectVolume.IsValid()) &&
(k.ImagePull == nil || k.ImagePull.IsValid()) &&
(k.NydusImage == nil || k.NydusImage.IsValid()) &&
(k.DmVerity == nil || k.DmVerity.IsValid() == nil)
}
func (d *DmVerityInfo) validateHashType() error {
switch strings.ToLower(d.HashType) {
case "sha256":
return d.isValidHash(64, "sha256")
case "sha1":
return d.isValidHash(40, "sha1")
default:
return fmt.Errorf("Unsupported hash algorithm %s for DmVerity device %s", d.HashType, d.Hash)
}
}
func isValidBlockSize(blockSize uint64) bool {
return minBlockSize <= blockSize && blockSize <= maxBlockSize
}
func (d *DmVerityInfo) isValidHash(expectedLen int, hashType string) error {
_, err := hex.DecodeString(d.Hash)
if len(d.Hash) != expectedLen || err != nil {
return fmt.Errorf("Invalid hash value %s:%s for DmVerity device with %s", hashType, d.Hash, hashType)
}
return nil
}
func ParseDmVerityInfo(option string) (*DmVerityInfo, error) {
no := &DmVerityInfo{}
if err := json.Unmarshal([]byte(option), no); err != nil {
return nil, errors.Wrapf(err, "DmVerityInfo json unmarshal err")
}
if err := no.IsValid(); err != nil {
return nil, fmt.Errorf("DmVerityInfo is not correct, %+v; error = %+v", no, err)
}
return no, nil
}
func ParseKataVirtualVolume(option string) (*KataVirtualVolume, error) {
opt, err := base64.StdEncoding.DecodeString(option)
if err != nil {
return nil, errors.Wrap(err, "KataVirtualVolume base64 decoding err")
}
no := &KataVirtualVolume{}
if err := json.Unmarshal(opt, no); err != nil {
return nil, errors.Wrapf(err, "KataVirtualVolume json unmarshal err")
}
if !no.IsValid() {
return nil, fmt.Errorf("KataVirtualVolume is not correct, %+v", no)
}
return no, nil
}

View File

@ -0,0 +1,246 @@
package types
import (
"encoding/base64"
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDmVerityInfoValidation(t *testing.T) {
TestData := []DmVerityInfo{
{
HashType: "md5", // "md5" is not a supported hash algorithm
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 3000, // Invalid block size, not a power of 2.
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 0, // Invalid block size, less than 512.
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 524800, // Invalid block size, greater than 524288.
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 3000, // Invalid hash block size, not a power of 2.
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 0, // Invalid hash block size, less than 512.
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 524800, // Invalid hash block size, greater than 524288.
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 0, // Invalid BlockNum, it must be greater than 0.
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 0, // Invalid offset, it must be greater than 0.
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 8193, // Invalid offset, it must be aligned to 512.
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608 - 4096, // Invalid offset, it must be equal to blocksize * BlockNum.
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
},
}
for _, d := range TestData {
assert.Error(t, d.IsValid())
}
TestCorrectData := DmVerityInfo{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
}
assert.NoError(t, TestCorrectData.IsValid())
}
func TestDirectAssignedVolumeValidation(t *testing.T) {
validDirectVolume := DirectAssignedVolume{
Metadata: map[string]string{"key": "value"},
}
assert.True(t, validDirectVolume.IsValid())
invalidDirectVolume := DirectAssignedVolume{
Metadata: nil,
}
assert.False(t, invalidDirectVolume.IsValid())
}
func TestImagePullVolumeValidation(t *testing.T) {
validImagePull := ImagePullVolume{
Metadata: map[string]string{"key": "value"},
}
assert.True(t, validImagePull.IsValid())
invalidImagePull := ImagePullVolume{
Metadata: nil,
}
assert.False(t, invalidImagePull.IsValid())
}
func TestNydusImageVolumeValidation(t *testing.T) {
validNydusImage := NydusImageVolume{
Config: "config_value",
SnapshotDir: "",
}
assert.True(t, validNydusImage.IsValid())
invalidNydusImage := NydusImageVolume{
Config: "",
SnapshotDir: "",
}
assert.False(t, invalidNydusImage.IsValid())
}
func TestKataVirtualVolumeValidation(t *testing.T) {
validKataVirtualVolume := KataVirtualVolume{
VolumeType: "direct_block",
Source: "/dev/sdb",
FSType: "ext4",
Options: []string{"rw"},
DirectVolume: &DirectAssignedVolume{
Metadata: map[string]string{"key": "value"},
},
// Initialize other fields
}
assert.True(t, validKataVirtualVolume.IsValid())
invalidKataVirtualVolume := KataVirtualVolume{
VolumeType: "direct_block",
Source: "/dev/sdb",
FSType: "",
Options: nil,
DirectVolume: &DirectAssignedVolume{
Metadata: nil,
},
// Initialize other fields
}
assert.False(t, invalidKataVirtualVolume.IsValid())
}
func TestParseDmVerityInfo(t *testing.T) {
// Create a mock valid KataVirtualVolume
validDmVerityInfo := DmVerityInfo{
HashType: "sha256",
Blocksize: 512,
Hashsize: 512,
BlockNum: 16384,
Offset: 8388608,
Hash: "9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174",
}
validKataVirtualVolumeJSON, _ := json.Marshal(validDmVerityInfo)
t.Run("Valid Option", func(t *testing.T) {
volume, err := ParseDmVerityInfo(string(validKataVirtualVolumeJSON))
assert.NoError(t, err)
assert.NotNil(t, volume)
assert.NoError(t, volume.IsValid())
})
t.Run("Invalid JSON Option", func(t *testing.T) {
volume, err := ParseDmVerityInfo("invalid_json")
assert.Error(t, err)
assert.Nil(t, volume)
})
}
func TestParseKataVirtualVolume(t *testing.T) {
// Create a mock valid KataVirtualVolume
validKataVirtualVolume := KataVirtualVolume{
VolumeType: "direct_block",
Source: "/dev/sdb",
FSType: "ext4",
Options: []string{"rw"},
DirectVolume: &DirectAssignedVolume{
Metadata: map[string]string{"key": "value"},
},
// Initialize other fields
}
validKataVirtualVolumeJSON, _ := json.Marshal(validKataVirtualVolume)
validOption := base64.StdEncoding.EncodeToString(validKataVirtualVolumeJSON)
t.Run("Valid Option", func(t *testing.T) {
volume, err := ParseKataVirtualVolume(validOption)
assert.NoError(t, err)
assert.NotNil(t, volume)
assert.True(t, volume.IsValid())
})
t.Run("Invalid JSON Option", func(t *testing.T) {
invalidJSONOption := base64.StdEncoding.EncodeToString([]byte("invalid_json"))
volume, err := ParseKataVirtualVolume(invalidJSONOption)
assert.Error(t, err)
assert.Nil(t, volume)
})
invalidBase64Option := "invalid_base64"
t.Run("Invalid Base64 Option", func(t *testing.T) {
volume, err := ParseKataVirtualVolume(invalidBase64Option)
assert.Error(t, err)
assert.Nil(t, volume)
})
}