empty_dir: Check if hugetlbfs volume is mounted with a correct pagesize

Extended GetMountMedium function to check if hugetlbfs volume
is mounted with the page size equal to the medium size.

Page size is obtained from the 'pagesize' mount option of the
mounted hugetlbfs volume.
This commit is contained in:
Ed Bartosh
2020-02-17 17:21:50 +02:00
parent 882d6e93af
commit 03ecc20b19
4 changed files with 423 additions and 24 deletions

View File

@@ -19,6 +19,8 @@ limitations under the License.
package emptydir
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -73,8 +75,8 @@ type fakeMountDetector struct {
isMount bool
}
func (fake *fakeMountDetector) GetMountMedium(path string) (v1.StorageMedium, bool, error) {
return fake.medium, fake.isMount, nil
func (fake *fakeMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
return fake.medium, fake.isMount, nil, nil
}
func TestPluginEmptyRootContext(t *testing.T) {
@@ -582,3 +584,331 @@ func TestGetHugePagesMountOptions(t *testing.T) {
})
}
}
type testMountDetector struct {
pageSize *resource.Quantity
isMnt bool
err error
}
func (md *testMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
return v1.StorageMediumHugePages, md.isMnt, md.pageSize, md.err
}
func TestSetupHugepages(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "TestSetupHugepages")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
pageSize2Mi := resource.MustParse("2Mi")
testCases := map[string]struct {
path string
ed *emptyDir
shouldFail bool
}{
"Valid: mount expected": {
path: tmpdir,
ed: &emptyDir{
medium: v1.StorageMediumHugePages,
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: &mount.FakeMounter{},
mountDetector: &testMountDetector{
pageSize: &resource.Quantity{},
isMnt: false,
err: nil,
},
},
shouldFail: false,
},
"Valid: already mounted with correct pagesize": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages-2Mi",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
mountDetector: &testMountDetector{
pageSize: &pageSize2Mi,
isMnt: true,
err: nil,
},
},
shouldFail: false,
},
"Valid: already mounted": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
mountDetector: &testMountDetector{
pageSize: nil,
isMnt: true,
err: nil,
},
},
shouldFail: false,
},
"Invalid: mounter is nil": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages-2Mi",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: nil,
},
shouldFail: true,
},
"Invalid: GetMountMedium error": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages-2Mi",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
mountDetector: &testMountDetector{
pageSize: &pageSize2Mi,
isMnt: true,
err: fmt.Errorf("GetMountMedium error"),
},
},
shouldFail: true,
},
"Invalid: medium and page size differ": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages-1Gi",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
},
},
mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
mountDetector: &testMountDetector{
pageSize: &pageSize2Mi,
isMnt: true,
err: nil,
},
},
shouldFail: true,
},
"Invalid medium": {
path: tmpdir,
ed: &emptyDir{
medium: "HugePages-NN",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: &mount.FakeMounter{},
mountDetector: &testMountDetector{
pageSize: &resource.Quantity{},
isMnt: false,
err: nil,
},
},
shouldFail: true,
},
"Invalid: setupDir fails": {
path: "",
ed: &emptyDir{
medium: v1.StorageMediumHugePages,
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
mounter: &mount.FakeMounter{},
},
shouldFail: true,
},
}
for testCaseName, testCase := range testCases {
t.Run(testCaseName, func(t *testing.T) {
err := testCase.ed.setupHugepages(testCase.path)
if testCase.shouldFail && err == nil {
t.Errorf("%s: Unexpected success", testCaseName)
} else if !testCase.shouldFail && err != nil {
t.Errorf("%s: Unexpected error: %v", testCaseName, err)
}
})
}
}
func TestGetPageSize(t *testing.T) {
mounter := &mount.FakeMounter{
MountPoints: []mount.MountPoint{
{
Device: "/dev/sda2",
Type: "ext4",
Path: "/",
Opts: []string{"rw", "relatime", "errors=remount-ro"},
},
{
Device: "/dev/hugepages",
Type: "hugetlbfs",
Path: "/mnt/hugepages-2Mi",
Opts: []string{"rw", "relatime", "pagesize=2M"},
},
{
Device: "sysfs",
Type: "sysfs",
Path: "/sys",
Opts: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
},
{
Device: "/dev/hugepages",
Type: "hugetlbfs",
Path: "/mnt/hugepages-1Gi",
Opts: []string{"rw", "relatime", "pagesize=1024M"},
},
{
Device: "/dev/hugepages",
Type: "hugetlbfs",
Path: "/mnt/noopt",
Opts: []string{"rw", "relatime"},
},
{
Device: "/dev/hugepages",
Type: "hugetlbfs",
Path: "/mnt/badopt",
Opts: []string{"rw", "relatime", "pagesize=NN"},
},
},
}
testCases := map[string]struct {
path string
mounter mount.Interface
expectedResult resource.Quantity
shouldFail bool
}{
"Valid: existing 2Mi mount": {
path: "/mnt/hugepages-2Mi",
mounter: mounter,
shouldFail: false,
expectedResult: resource.MustParse("2Mi"),
},
"Valid: existing 1Gi mount": {
path: "/mnt/hugepages-1Gi",
mounter: mounter,
shouldFail: false,
expectedResult: resource.MustParse("1Gi"),
},
"Invalid: mount point doesn't exist": {
path: "/mnt/nomp",
mounter: mounter,
shouldFail: true,
},
"Invalid: no pagesize option": {
path: "/mnt/noopt",
mounter: mounter,
shouldFail: true,
},
"Invalid: incorrect pagesize option": {
path: "/mnt/badopt",
mounter: mounter,
shouldFail: true,
},
}
for testCaseName, testCase := range testCases {
t.Run(testCaseName, func(t *testing.T) {
pageSize, err := getPageSize(testCase.path, testCase.mounter)
if testCase.shouldFail && err == nil {
t.Errorf("%s: Unexpected success", testCaseName)
} else if !testCase.shouldFail && err != nil {
t.Errorf("%s: Unexpected error: %v", testCaseName, err)
}
if err == nil && pageSize.Cmp(testCase.expectedResult) != 0 {
t.Errorf("%s: Unexpected result: %s, expected: %s", testCaseName, pageSize.String(), testCase.expectedResult.String())
}
})
}
}