123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // +build linux
- package quota
- import (
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
- "github.com/gotestyourself/gotestyourself/fs"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/sys/unix"
- )
- // 10MB
- const testQuotaSize = 10 * 1024 * 1024
- const imageSize = 64 * 1024 * 1024
- func TestBlockDev(t *testing.T) {
- mkfs, err := exec.LookPath("mkfs.xfs")
- if err != nil {
- t.Skip("mkfs.xfs not found in PATH")
- }
- // create a sparse image
- imageFile, err := ioutil.TempFile("", "xfs-image")
- if err != nil {
- t.Fatal(err)
- }
- imageFileName := imageFile.Name()
- defer os.Remove(imageFileName)
- if _, err = imageFile.Seek(imageSize-1, 0); err != nil {
- t.Fatal(err)
- }
- if _, err = imageFile.Write([]byte{0}); err != nil {
- t.Fatal(err)
- }
- if err = imageFile.Close(); err != nil {
- t.Fatal(err)
- }
- // The reason for disabling these options is sometimes people run with a newer userspace
- // than kernelspace
- out, err := exec.Command(mkfs, "-m", "crc=0,finobt=0", imageFileName).CombinedOutput()
- if len(out) > 0 {
- t.Log(string(out))
- }
- if err != nil {
- t.Fatal(err)
- }
- t.Run("testBlockDevQuotaDisabled", wrapMountTest(imageFileName, false, testBlockDevQuotaDisabled))
- t.Run("testBlockDevQuotaEnabled", wrapMountTest(imageFileName, true, testBlockDevQuotaEnabled))
- t.Run("testSmallerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testSmallerThanQuota)))
- t.Run("testBiggerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testBiggerThanQuota)))
- t.Run("testRetrieveQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testRetrieveQuota)))
- }
- func wrapMountTest(imageFileName string, enableQuota bool, testFunc func(t *testing.T, mountPoint, backingFsDev string)) func(*testing.T) {
- return func(t *testing.T) {
- mountOptions := "loop"
- if enableQuota {
- mountOptions = mountOptions + ",prjquota"
- }
- mountPointDir := fs.NewDir(t, "xfs-mountPoint")
- defer mountPointDir.Remove()
- mountPoint := mountPointDir.Path()
- out, err := exec.Command("mount", "-o", mountOptions, imageFileName, mountPoint).CombinedOutput()
- if err != nil {
- _, err := os.Stat("/proc/fs/xfs")
- if os.IsNotExist(err) {
- t.Skip("no /proc/fs/xfs")
- }
- }
- require.NoError(t, err, "mount failed: %s", out)
- defer func() {
- require.NoError(t, unix.Unmount(mountPoint, 0))
- }()
- backingFsDev, err := makeBackingFsDev(mountPoint)
- require.NoError(t, err)
- testFunc(t, mountPoint, backingFsDev)
- }
- }
- func testBlockDevQuotaDisabled(t *testing.T, mountPoint, backingFsDev string) {
- hasSupport, err := hasQuotaSupport(backingFsDev)
- require.NoError(t, err)
- assert.False(t, hasSupport)
- }
- func testBlockDevQuotaEnabled(t *testing.T, mountPoint, backingFsDev string) {
- hasSupport, err := hasQuotaSupport(backingFsDev)
- require.NoError(t, err)
- assert.True(t, hasSupport)
- }
- func wrapQuotaTest(testFunc func(t *testing.T, ctrl *Control, mountPoint, testDir, testSubDir string)) func(t *testing.T, mountPoint, backingFsDev string) {
- return func(t *testing.T, mountPoint, backingFsDev string) {
- testDir, err := ioutil.TempDir(mountPoint, "per-test")
- require.NoError(t, err)
- defer os.RemoveAll(testDir)
- ctrl, err := NewControl(testDir)
- require.NoError(t, err)
- testSubDir, err := ioutil.TempDir(testDir, "quota-test")
- require.NoError(t, err)
- testFunc(t, ctrl, mountPoint, testDir, testSubDir)
- }
- }
- func testSmallerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
- require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
- smallerThanQuotaFile := filepath.Join(testSubDir, "smaller-than-quota")
- require.NoError(t, ioutil.WriteFile(smallerThanQuotaFile, make([]byte, testQuotaSize/2), 0644))
- require.NoError(t, os.Remove(smallerThanQuotaFile))
- }
- func testBiggerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
- // Make sure the quota is being enforced
- // TODO: When we implement this under EXT4, we need to shed CAP_SYS_RESOURCE, otherwise
- // we're able to violate quota without issue
- require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
- biggerThanQuotaFile := filepath.Join(testSubDir, "bigger-than-quota")
- err := ioutil.WriteFile(biggerThanQuotaFile, make([]byte, testQuotaSize+1), 0644)
- require.Error(t, err)
- if err == io.ErrShortWrite {
- require.NoError(t, os.Remove(biggerThanQuotaFile))
- }
- }
- func testRetrieveQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) {
- // Validate that we can retrieve quota
- require.NoError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize}))
- var q Quota
- require.NoError(t, ctrl.GetQuota(testSubDir, &q))
- assert.EqualValues(t, testQuotaSize, q.Size)
- }
|