mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	Errors from staticcheck: pkg/volume/azure_dd/azure_common.go:68:2: var winDiskNumFormat is unused (U1000) pkg/volume/csi/csi_block.go:97:2: field volumeInfo is unused (U1000) pkg/volume/csi/csi_block_test.go:56:6: func prepareBlockUnmapperTest is unused (U1000) pkg/volume/csi/csi_client.go:108:2: const initialDuration is unused (U1000) pkg/volume/csi/csi_client.go:109:2: const factor is unused (U1000) pkg/volume/csi/csi_client.go:110:2: const steps is unused (U1000) pkg/volume/csi/csi_client_test.go:83:8: this value of err is never used (SA4006) pkg/volume/csi/csi_mounter.go:76:2: field options is unused (U1000) pkg/volume/csi/csi_mounter_test.go:454:13: this value of err is never used (SA4006) pkg/volume/csi/csi_plugin_test.go:766:16: this value of err is never used (SA4006) pkg/volume/csi/csi_plugin_test.go:861:13: this value of err is never used (SA4006) pkg/volume/csi/csi_plugin_test.go:1186:13: this value of err is never used (SA4006) pkg/volume/csi/csi_plugin_test.go:1249:13: this value of err is never used (SA4006) pkg/volume/csi/csi_test.go:305:5: the goroutine calls T.Fatalf, which must be called in the same goroutine as the test (SA2002) pkg/volume/flexvolume/probe_test.go:67:10: this value of err is never used (SA4006) pkg/volume/iscsi/iscsi_test.go:95:2: field attachCalled is unused (U1000) pkg/volume/iscsi/iscsi_test.go:96:2: field detachCalled is unused (U1000) pkg/volume/iscsi/iscsi_test.go:501:24: this value of err is never used (SA4006) pkg/volume/iscsi/iscsi_util_test.go:159:2: this value of exist is never used (SA4006) pkg/volume/local/local.go:351:57: argument devicePath is overwritten before first use (SA4009) pkg/volume/plugins_test.go:119:2: this value of plug is never used (SA4006) pkg/volume/plugins_test.go:125:2: this value of plug is never used (SA4006) pkg/volume/quobyte/quobyte.go:474:23: this result of append is never used, except maybe in other appends (SA4010) pkg/volume/quobyte/quobyte.go:477:23: this result of append is never used, except maybe in other appends (SA4010) pkg/volume/quobyte/quobyte.go:480:23: this result of append is never used, except maybe in other appends (SA4010) pkg/volume/rbd/rbd.go:886:2: field adminSecret is unused (U1000) pkg/volume/rbd/rbd.go:887:2: field adminID is unused (U1000) pkg/volume/rbd/rbd.go:888:2: field imageFormat is unused (U1000) pkg/volume/rbd/rbd.go:889:2: field imageFeatures is unused (U1000) pkg/volume/storageos/storageos.go:302:2: field secretName is unused (U1000) pkg/volume/storageos/storageos_util_test.go:43:2: field apiAddr is unused (U1000) pkg/volume/storageos/storageos_util_test.go:44:2: field apiUser is unused (U1000) pkg/volume/storageos/storageos_util_test.go:45:2: field apiPass is unused (U1000) pkg/volume/storageos/storageos_util_test.go:46:2: field apiVersion is unused (U1000) pkg/volume/util/atomic_writer_test.go:756:49: argument err is overwritten before first use (SA4009) pkg/volume/util/fsquota/common/quota_linux_common.go:37:2: const acct is unused (U1000) pkg/volume/util/fsquota/common/quota_linux_common.go:38:2: const enforcing is unused (U1000) pkg/volume/util/fsquota/project.go:168:31: identical expressions on the left and right side of the '==' operator (SA4000) pkg/volume/util/fsquota/quota_linux.go:306:50: argument poduid is overwritten before first use (SA4009) pkg/volume/util/fsquota/quota_linux_test.go:558:16: this value of err is never used (SA4006) pkg/volume/util/subpath/subpath_linux.go:232:81: argument err is overwritten before first use (SA4009) pkg/volume/util/subpath/subpath_linux_test.go:579:73: argument err is overwritten before first use (SA4009)
		
			
				
	
	
		
			737 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			737 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build linux
 | |
| 
 | |
| /*
 | |
| Copyright 2018 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package fsquota
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"k8s.io/utils/mount"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/api/resource"
 | |
| 	"k8s.io/apimachinery/pkg/types"
 | |
| 	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | |
| 	featuregatetesting "k8s.io/component-base/featuregate/testing"
 | |
| 	"k8s.io/kubernetes/pkg/features"
 | |
| 	"k8s.io/kubernetes/pkg/volume/util/fsquota/common"
 | |
| )
 | |
| 
 | |
| const dummyMountData = `sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
 | |
| proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
 | |
| devtmpfs /dev devtmpfs rw,nosuid,size=6133536k,nr_inodes=1533384,mode=755 0 0
 | |
| tmpfs /tmp tmpfs rw,nosuid,nodev 0 0
 | |
| /dev/sda1 /boot ext4 rw,relatime 0 0
 | |
| /dev/mapper/fedora-root / ext4 rw,noatime 0 0
 | |
| /dev/mapper/fedora-home /home ext4 rw,noatime 0 0
 | |
| /dev/sdb1 /virt xfs rw,noatime,attr2,inode64,usrquota,prjquota 0 0
 | |
| `
 | |
| 
 | |
| func dummyFakeMount1() mount.Interface {
 | |
| 	return mount.NewFakeMounter(
 | |
| 		[]mount.MountPoint{
 | |
| 			{
 | |
| 				Device: "tmpfs",
 | |
| 				Path:   "/tmp",
 | |
| 				Type:   "tmpfs",
 | |
| 				Opts:   []string{"rw", "nosuid", "nodev"},
 | |
| 			},
 | |
| 			{
 | |
| 				Device: "/dev/sda1",
 | |
| 				Path:   "/boot",
 | |
| 				Type:   "ext4",
 | |
| 				Opts:   []string{"rw", "relatime"},
 | |
| 			},
 | |
| 			{
 | |
| 				Device: "/dev/mapper/fedora-root",
 | |
| 				Path:   "/",
 | |
| 				Type:   "ext4",
 | |
| 				Opts:   []string{"rw", "relatime"},
 | |
| 			},
 | |
| 			{
 | |
| 				Device: "/dev/mapper/fedora-home",
 | |
| 				Path:   "/home",
 | |
| 				Type:   "ext4",
 | |
| 				Opts:   []string{"rw", "relatime"},
 | |
| 			},
 | |
| 			{
 | |
| 				Device: "/dev/sdb1",
 | |
| 				Path:   "/mnt/virt",
 | |
| 				Type:   "xfs",
 | |
| 				Opts:   []string{"rw", "relatime", "attr2", "inode64", "usrquota", "prjquota"},
 | |
| 			},
 | |
| 		})
 | |
| }
 | |
| 
 | |
| type backingDevTest struct {
 | |
| 	path           string
 | |
| 	mountdata      string
 | |
| 	expectedResult string
 | |
| 	expectFailure  bool
 | |
| }
 | |
| 
 | |
| type mountpointTest struct {
 | |
| 	path           string
 | |
| 	mounter        mount.Interface
 | |
| 	expectedResult string
 | |
| 	expectFailure  bool
 | |
| }
 | |
| 
 | |
| func testBackingDev1(testcase backingDevTest) error {
 | |
| 	tmpfile, err := ioutil.TempFile("", "backingdev")
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer os.Remove(tmpfile.Name())
 | |
| 	if _, err = tmpfile.WriteString(testcase.mountdata); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	backingDev, err := detectBackingDevInternal(testcase.path, tmpfile.Name())
 | |
| 	if err != nil {
 | |
| 		if testcase.expectFailure {
 | |
| 			return nil
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	if testcase.expectFailure {
 | |
| 		return fmt.Errorf("Path %s expected to fail; succeeded and got %s", testcase.path, backingDev)
 | |
| 	}
 | |
| 	if backingDev == testcase.expectedResult {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return fmt.Errorf("Mismatch: path %s expects mountpoint %s got %s", testcase.path, testcase.expectedResult, backingDev)
 | |
| }
 | |
| 
 | |
| func TestBackingDev(t *testing.T) {
 | |
| 	testcasesBackingDev := map[string]backingDevTest{
 | |
| 		"Root": {
 | |
| 			"/",
 | |
| 			dummyMountData,
 | |
| 			"/dev/mapper/fedora-root",
 | |
| 			false,
 | |
| 		},
 | |
| 		"tmpfs": {
 | |
| 			"/tmp",
 | |
| 			dummyMountData,
 | |
| 			"tmpfs",
 | |
| 			false,
 | |
| 		},
 | |
| 		"user filesystem": {
 | |
| 			"/virt",
 | |
| 			dummyMountData,
 | |
| 			"/dev/sdb1",
 | |
| 			false,
 | |
| 		},
 | |
| 		"empty mountpoint": {
 | |
| 			"",
 | |
| 			dummyMountData,
 | |
| 			"",
 | |
| 			true,
 | |
| 		},
 | |
| 		"bad mountpoint": {
 | |
| 			"/kiusf",
 | |
| 			dummyMountData,
 | |
| 			"",
 | |
| 			true,
 | |
| 		},
 | |
| 	}
 | |
| 	for name, testcase := range testcasesBackingDev {
 | |
| 		err := testBackingDev1(testcase)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("%s failed: %s", name, err.Error())
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDetectMountPoint(t *testing.T) {
 | |
| 	testcasesMount := map[string]mountpointTest{
 | |
| 		"Root": {
 | |
| 			"/",
 | |
| 			dummyFakeMount1(),
 | |
| 			"/",
 | |
| 			false,
 | |
| 		},
 | |
| 		"(empty)": {
 | |
| 			"",
 | |
| 			dummyFakeMount1(),
 | |
| 			"/",
 | |
| 			false,
 | |
| 		},
 | |
| 		"(invalid)": {
 | |
| 			"",
 | |
| 			dummyFakeMount1(),
 | |
| 			"/",
 | |
| 			false,
 | |
| 		},
 | |
| 		"/usr": {
 | |
| 			"/usr",
 | |
| 			dummyFakeMount1(),
 | |
| 			"/",
 | |
| 			false,
 | |
| 		},
 | |
| 		"/var/tmp": {
 | |
| 			"/var/tmp",
 | |
| 			dummyFakeMount1(),
 | |
| 			"/",
 | |
| 			false,
 | |
| 		},
 | |
| 	}
 | |
| 	for name, testcase := range testcasesMount {
 | |
| 		mountpoint, err := detectMountpointInternal(testcase.mounter, testcase.path)
 | |
| 		if err == nil && testcase.expectFailure {
 | |
| 			t.Errorf("Case %s expected failure, but succeeded, returning mountpoint %s", name, mountpoint)
 | |
| 		} else if err != nil {
 | |
| 			t.Errorf("Case %s failed: %s", name, err.Error())
 | |
| 		} else if mountpoint != testcase.expectedResult {
 | |
| 			t.Errorf("Case %s got mountpoint %s, expected %s", name, mountpoint, testcase.expectedResult)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var dummyMountPoints = []mount.MountPoint{
 | |
| 	{
 | |
| 		Device: "/dev/sda2",
 | |
| 		Path:   "/quota1",
 | |
| 		Type:   "ext4",
 | |
| 		Opts:   []string{"rw", "relatime", "prjquota"},
 | |
| 	},
 | |
| 	{
 | |
| 		Device: "/dev/sda3",
 | |
| 		Path:   "/quota2",
 | |
| 		Type:   "ext4",
 | |
| 		Opts:   []string{"rw", "relatime", "prjquota"},
 | |
| 	},
 | |
| 	{
 | |
| 		Device: "/dev/sda3",
 | |
| 		Path:   "/noquota",
 | |
| 		Type:   "ext4",
 | |
| 		Opts:   []string{"rw", "relatime"},
 | |
| 	},
 | |
| 	{
 | |
| 		Device: "/dev/sda1",
 | |
| 		Path:   "/",
 | |
| 		Type:   "ext4",
 | |
| 		Opts:   []string{"rw", "relatime"},
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func dummyQuotaTest() mount.Interface {
 | |
| 	return mount.NewFakeMounter(dummyMountPoints)
 | |
| }
 | |
| 
 | |
| func dummySetFSInfo(path string) {
 | |
| 	if enabledQuotasForMonitoring() {
 | |
| 		for _, mount := range dummyMountPoints {
 | |
| 			if strings.HasPrefix(path, mount.Path) {
 | |
| 				mountpointMap[path] = mount.Path
 | |
| 				backingDevMap[path] = mount.Device
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type VolumeProvider1 struct {
 | |
| }
 | |
| 
 | |
| type VolumeProvider2 struct {
 | |
| }
 | |
| 
 | |
| type testVolumeQuota struct {
 | |
| }
 | |
| 
 | |
| func logAllMaps(where string) {
 | |
| 	fmt.Printf("Maps at %s\n", where)
 | |
| 	fmt.Printf("    Map podQuotaMap contents:\n")
 | |
| 	for key, val := range podQuotaMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map dirQuotaMap contents:\n")
 | |
| 	for key, val := range dirQuotaMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map quotaPodMap contents:\n")
 | |
| 	for key, val := range quotaPodMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map dirPodMap contents:\n")
 | |
| 	for key, val := range dirPodMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map devApplierMap contents:\n")
 | |
| 	for key, val := range devApplierMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map dirApplierMap contents:\n")
 | |
| 	for key, val := range dirApplierMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map podDirCountMap contents:\n")
 | |
| 	for key, val := range podDirCountMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map quotaSizeMap contents:\n")
 | |
| 	for key, val := range quotaSizeMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map supportsQuotasMap contents:\n")
 | |
| 	for key, val := range supportsQuotasMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map backingDevMap contents:\n")
 | |
| 	for key, val := range backingDevMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("    Map mountpointMap contents:\n")
 | |
| 	for key, val := range mountpointMap {
 | |
| 		fmt.Printf("        %v -> %v\n", key, val)
 | |
| 	}
 | |
| 	fmt.Printf("End maps %s\n", where)
 | |
| }
 | |
| 
 | |
| var testIDQuotaMap = make(map[common.QuotaID]string)
 | |
| var testQuotaIDMap = make(map[string]common.QuotaID)
 | |
| 
 | |
| func (*VolumeProvider1) GetQuotaApplier(mountpoint string, backingDev string) common.LinuxVolumeQuotaApplier {
 | |
| 	if strings.HasPrefix(mountpoint, "/quota1") {
 | |
| 		return testVolumeQuota{}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (*VolumeProvider2) GetQuotaApplier(mountpoint string, backingDev string) common.LinuxVolumeQuotaApplier {
 | |
| 	if strings.HasPrefix(mountpoint, "/quota2") {
 | |
| 		return testVolumeQuota{}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (v testVolumeQuota) SetQuotaOnDir(dir string, id common.QuotaID, _ int64) error {
 | |
| 	odir, ok := testIDQuotaMap[id]
 | |
| 	if ok && dir != odir {
 | |
| 		return fmt.Errorf("ID %v is already in use", id)
 | |
| 	}
 | |
| 	oid, ok := testQuotaIDMap[dir]
 | |
| 	if ok && id != oid {
 | |
| 		return fmt.Errorf("Directory %s already has a quota applied", dir)
 | |
| 	}
 | |
| 	testQuotaIDMap[dir] = id
 | |
| 	testIDQuotaMap[id] = dir
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (v testVolumeQuota) GetQuotaOnDir(path string) (common.QuotaID, error) {
 | |
| 	id, ok := testQuotaIDMap[path]
 | |
| 	if ok {
 | |
| 		return id, nil
 | |
| 	}
 | |
| 	return common.BadQuotaID, fmt.Errorf("No quota available for %s", path)
 | |
| }
 | |
| 
 | |
| func (v testVolumeQuota) QuotaIDIsInUse(id common.QuotaID) (bool, error) {
 | |
| 	if _, ok := testIDQuotaMap[id]; ok {
 | |
| 		return true, nil
 | |
| 	}
 | |
| 	// So that we reject some
 | |
| 	if id%3 == 0 {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	return false, nil
 | |
| }
 | |
| 
 | |
| func (v testVolumeQuota) GetConsumption(_ string, _ common.QuotaID) (int64, error) {
 | |
| 	return 4096, nil
 | |
| }
 | |
| 
 | |
| func (v testVolumeQuota) GetInodes(_ string, _ common.QuotaID) (int64, error) {
 | |
| 	return 1, nil
 | |
| }
 | |
| 
 | |
| func fakeSupportsQuotas(path string) (bool, error) {
 | |
| 	dummySetFSInfo(path)
 | |
| 	return SupportsQuotas(dummyQuotaTest(), path)
 | |
| }
 | |
| 
 | |
| func fakeAssignQuota(path string, poduid types.UID, bytes int64) error {
 | |
| 	dummySetFSInfo(path)
 | |
| 	return AssignQuota(dummyQuotaTest(), path, poduid, resource.NewQuantity(bytes, resource.DecimalSI))
 | |
| }
 | |
| 
 | |
| func fakeClearQuota(path string) error {
 | |
| 	dummySetFSInfo(path)
 | |
| 	return ClearQuota(dummyQuotaTest(), path)
 | |
| }
 | |
| 
 | |
| type quotaTestCase struct {
 | |
| 	path                             string
 | |
| 	poduid                           types.UID
 | |
| 	bytes                            int64
 | |
| 	op                               string
 | |
| 	expectedProjects                 string
 | |
| 	expectedProjid                   string
 | |
| 	supportsQuota                    bool
 | |
| 	expectsSetQuota                  bool
 | |
| 	deltaExpectedPodQuotaCount       int
 | |
| 	deltaExpectedDirQuotaCount       int
 | |
| 	deltaExpectedQuotaPodCount       int
 | |
| 	deltaExpectedDirPodCount         int
 | |
| 	deltaExpectedDevApplierCount     int
 | |
| 	deltaExpectedDirApplierCount     int
 | |
| 	deltaExpectedPodDirCountCount    int
 | |
| 	deltaExpectedQuotaSizeCount      int
 | |
| 	deltaExpectedSupportsQuotasCount int
 | |
| 	deltaExpectedBackingDevCount     int
 | |
| 	deltaExpectedMountpointCount     int
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	projectsHeader = `# This is a /etc/projects header
 | |
| 1048578:/quota/d
 | |
| `
 | |
| 	projects1 = `1048577:/quota1/a
 | |
| `
 | |
| 	projects2 = `1048577:/quota1/a
 | |
| 1048580:/quota1/b
 | |
| `
 | |
| 	projects3 = `1048577:/quota1/a
 | |
| 1048580:/quota1/b
 | |
| 1048581:/quota2/b
 | |
| `
 | |
| 	projects4 = `1048577:/quota1/a
 | |
| 1048581:/quota2/b
 | |
| `
 | |
| 	projects5 = `1048581:/quota2/b
 | |
| `
 | |
| 
 | |
| 	projidHeader = `# This is a /etc/projid header
 | |
| xxxxxx:1048579
 | |
| `
 | |
| 	projid1 = `volume1048577:1048577
 | |
| `
 | |
| 	projid2 = `volume1048577:1048577
 | |
| volume1048580:1048580
 | |
| `
 | |
| 	projid3 = `volume1048577:1048577
 | |
| volume1048580:1048580
 | |
| volume1048581:1048581
 | |
| `
 | |
| 	projid4 = `volume1048577:1048577
 | |
| volume1048581:1048581
 | |
| `
 | |
| 	projid5 = `volume1048581:1048581
 | |
| `
 | |
| )
 | |
| 
 | |
| var quotaTestCases = []quotaTestCase{
 | |
| 	{
 | |
| 		"/quota1/a", "", 1024, "Supports", "", "",
 | |
| 		true, true, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/a", "", 1024, "Set", projects1, projid1,
 | |
| 		true, true, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/b", "x", 1024, "Set", projects2, projid2,
 | |
| 		true, true, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota2/b", "x", 1024, "Set", projects3, projid3,
 | |
| 		true, true, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/b", "x", 1024, "Set", projects3, projid3,
 | |
| 		true, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/b", "", 1024, "Clear", projects4, projid4,
 | |
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
 | |
| 	},
 | |
| 	{
 | |
| 		"/noquota/a", "", 1024, "Supports", projects4, projid4,
 | |
| 		false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/a", "", 1024, "Clear", projects5, projid5,
 | |
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota1/a", "", 1024, "Clear", projects5, projid5,
 | |
| 		true, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
| 	},
 | |
| 	{
 | |
| 		"/quota2/b", "", 1024, "Clear", "", "",
 | |
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| func compareProjectsFiles(t *testing.T, testcase quotaTestCase, projectsFile string, projidFile string, enabled bool) {
 | |
| 	bytes, err := ioutil.ReadFile(projectsFile)
 | |
| 	if err != nil {
 | |
| 		t.Error(err.Error())
 | |
| 	} else {
 | |
| 		s := string(bytes)
 | |
| 		p := projectsHeader
 | |
| 		if enabled {
 | |
| 			p += testcase.expectedProjects
 | |
| 		}
 | |
| 		if s != p {
 | |
| 			t.Errorf("Case %v /etc/projects miscompare: expected\n`%s`\ngot\n`%s`\n", testcase.path, p, s)
 | |
| 		}
 | |
| 	}
 | |
| 	bytes, err = ioutil.ReadFile(projidFile)
 | |
| 	if err != nil {
 | |
| 		t.Error(err.Error())
 | |
| 	} else {
 | |
| 		s := string(bytes)
 | |
| 		p := projidHeader
 | |
| 		if enabled {
 | |
| 			p += testcase.expectedProjid
 | |
| 		}
 | |
| 		if s != p {
 | |
| 			t.Errorf("Case %v /etc/projid miscompare: expected\n`%s`\ngot\n`%s`\n", testcase.path, p, s)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func runCaseEnabled(t *testing.T, testcase quotaTestCase, seq int) bool {
 | |
| 	fail := false
 | |
| 	var err error
 | |
| 	switch testcase.op {
 | |
| 	case "Supports":
 | |
| 		supports, err := fakeSupportsQuotas(testcase.path)
 | |
| 		if err != nil {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) Got error in fakeSupportsQuotas: %v", seq, testcase.path, true, err)
 | |
| 		}
 | |
| 		if supports != testcase.supportsQuota {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) fakeSupportsQuotas got %v, expect %v", seq, testcase.path, true, supports, testcase.supportsQuota)
 | |
| 		}
 | |
| 		return fail
 | |
| 	case "Set":
 | |
| 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes)
 | |
| 	case "Clear":
 | |
| 		err = fakeClearQuota(testcase.path)
 | |
| 	case "GetConsumption":
 | |
| 		_, err = GetConsumption(testcase.path)
 | |
| 	case "GetInodes":
 | |
| 		_, err = GetInodes(testcase.path)
 | |
| 	default:
 | |
| 		t.Errorf("Case %v (%s, %v) unknown operation %s", seq, testcase.path, true, testcase.op)
 | |
| 		return true
 | |
| 	}
 | |
| 	if err != nil && testcase.expectsSetQuota {
 | |
| 		fail = true
 | |
| 		t.Errorf("Case %v (%s, %v) %s expected to clear quota but failed %v", seq, testcase.path, true, testcase.op, err)
 | |
| 	} else if err == nil && !testcase.expectsSetQuota {
 | |
| 		fail = true
 | |
| 		t.Errorf("Case %v (%s, %v) %s expected not to clear quota but succeeded", seq, testcase.path, true, testcase.op)
 | |
| 	}
 | |
| 	return fail
 | |
| }
 | |
| 
 | |
| func runCaseDisabled(t *testing.T, testcase quotaTestCase, seq int) bool {
 | |
| 	var err error
 | |
| 	var supports bool
 | |
| 	switch testcase.op {
 | |
| 	case "Supports":
 | |
| 		if supports, _ = fakeSupportsQuotas(testcase.path); supports {
 | |
| 			t.Errorf("Case %v (%s, %v) supports quotas but shouldn't", seq, testcase.path, false)
 | |
| 			return true
 | |
| 		}
 | |
| 		return false
 | |
| 	case "Set":
 | |
| 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes)
 | |
| 	case "Clear":
 | |
| 		err = fakeClearQuota(testcase.path)
 | |
| 	case "GetConsumption":
 | |
| 		_, err = GetConsumption(testcase.path)
 | |
| 	case "GetInodes":
 | |
| 		_, err = GetInodes(testcase.path)
 | |
| 	default:
 | |
| 		t.Errorf("Case %v (%s, %v) unknown operation %s", seq, testcase.path, false, testcase.op)
 | |
| 		return true
 | |
| 	}
 | |
| 	if err == nil {
 | |
| 		t.Errorf("Case %v (%s, %v) %s: supports quotas but shouldn't", seq, testcase.path, false, testcase.op)
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func testAddRemoveQuotas(t *testing.T, enabled bool) {
 | |
| 	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LocalStorageCapacityIsolationFSQuotaMonitoring, enabled)()
 | |
| 	tmpProjectsFile, err := ioutil.TempFile("", "projects")
 | |
| 	if err == nil {
 | |
| 		_, err = tmpProjectsFile.WriteString(projectsHeader)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Unable to create fake projects file")
 | |
| 	}
 | |
| 	projectsFile = tmpProjectsFile.Name()
 | |
| 	tmpProjectsFile.Close()
 | |
| 	tmpProjidFile, err := ioutil.TempFile("", "projid")
 | |
| 	if err == nil {
 | |
| 		_, err = tmpProjidFile.WriteString(projidHeader)
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Unable to create fake projid file")
 | |
| 	}
 | |
| 	projidFile = tmpProjidFile.Name()
 | |
| 	tmpProjidFile.Close()
 | |
| 	providers = []common.LinuxVolumeQuotaProvider{
 | |
| 		&VolumeProvider1{},
 | |
| 		&VolumeProvider2{},
 | |
| 	}
 | |
| 	for k := range podQuotaMap {
 | |
| 		delete(podQuotaMap, k)
 | |
| 	}
 | |
| 	for k := range dirQuotaMap {
 | |
| 		delete(dirQuotaMap, k)
 | |
| 	}
 | |
| 	for k := range quotaPodMap {
 | |
| 		delete(quotaPodMap, k)
 | |
| 	}
 | |
| 	for k := range dirPodMap {
 | |
| 		delete(dirPodMap, k)
 | |
| 	}
 | |
| 	for k := range devApplierMap {
 | |
| 		delete(devApplierMap, k)
 | |
| 	}
 | |
| 	for k := range dirApplierMap {
 | |
| 		delete(dirApplierMap, k)
 | |
| 	}
 | |
| 	for k := range podDirCountMap {
 | |
| 		delete(podDirCountMap, k)
 | |
| 	}
 | |
| 	for k := range quotaSizeMap {
 | |
| 		delete(quotaSizeMap, k)
 | |
| 	}
 | |
| 	for k := range supportsQuotasMap {
 | |
| 		delete(supportsQuotasMap, k)
 | |
| 	}
 | |
| 	for k := range backingDevMap {
 | |
| 		delete(backingDevMap, k)
 | |
| 	}
 | |
| 	for k := range mountpointMap {
 | |
| 		delete(mountpointMap, k)
 | |
| 	}
 | |
| 	for k := range testIDQuotaMap {
 | |
| 		delete(testIDQuotaMap, k)
 | |
| 	}
 | |
| 	for k := range testQuotaIDMap {
 | |
| 		delete(testQuotaIDMap, k)
 | |
| 	}
 | |
| 	expectedPodQuotaCount := 0
 | |
| 	expectedDirQuotaCount := 0
 | |
| 	expectedQuotaPodCount := 0
 | |
| 	expectedDirPodCount := 0
 | |
| 	expectedDevApplierCount := 0
 | |
| 	expectedDirApplierCount := 0
 | |
| 	expectedPodDirCountCount := 0
 | |
| 	expectedQuotaSizeCount := 0
 | |
| 	expectedSupportsQuotasCount := 0
 | |
| 	expectedBackingDevCount := 0
 | |
| 	expectedMountpointCount := 0
 | |
| 	for seq, testcase := range quotaTestCases {
 | |
| 		if enabled {
 | |
| 			expectedPodQuotaCount += testcase.deltaExpectedPodQuotaCount
 | |
| 			expectedDirQuotaCount += testcase.deltaExpectedDirQuotaCount
 | |
| 			expectedQuotaPodCount += testcase.deltaExpectedQuotaPodCount
 | |
| 			expectedDirPodCount += testcase.deltaExpectedDirPodCount
 | |
| 			expectedDevApplierCount += testcase.deltaExpectedDevApplierCount
 | |
| 			expectedDirApplierCount += testcase.deltaExpectedDirApplierCount
 | |
| 			expectedPodDirCountCount += testcase.deltaExpectedPodDirCountCount
 | |
| 			expectedQuotaSizeCount += testcase.deltaExpectedQuotaSizeCount
 | |
| 			expectedSupportsQuotasCount += testcase.deltaExpectedSupportsQuotasCount
 | |
| 			expectedBackingDevCount += testcase.deltaExpectedBackingDevCount
 | |
| 			expectedMountpointCount += testcase.deltaExpectedMountpointCount
 | |
| 		}
 | |
| 		fail := false
 | |
| 		if enabled {
 | |
| 			fail = runCaseEnabled(t, testcase, seq)
 | |
| 		} else {
 | |
| 			fail = runCaseDisabled(t, testcase, seq)
 | |
| 		}
 | |
| 
 | |
| 		compareProjectsFiles(t, testcase, projectsFile, projidFile, enabled)
 | |
| 		if len(podQuotaMap) != expectedPodQuotaCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) podQuotaCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(podQuotaMap), expectedPodQuotaCount)
 | |
| 		}
 | |
| 		if len(dirQuotaMap) != expectedDirQuotaCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) dirQuotaCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(dirQuotaMap), expectedDirQuotaCount)
 | |
| 		}
 | |
| 		if len(quotaPodMap) != expectedQuotaPodCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) quotaPodCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(quotaPodMap), expectedQuotaPodCount)
 | |
| 		}
 | |
| 		if len(dirPodMap) != expectedDirPodCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) dirPodCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(dirPodMap), expectedDirPodCount)
 | |
| 		}
 | |
| 		if len(devApplierMap) != expectedDevApplierCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) devApplierCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(devApplierMap), expectedDevApplierCount)
 | |
| 		}
 | |
| 		if len(dirApplierMap) != expectedDirApplierCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) dirApplierCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(dirApplierMap), expectedDirApplierCount)
 | |
| 		}
 | |
| 		if len(podDirCountMap) != expectedPodDirCountCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) podDirCountCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(podDirCountMap), expectedPodDirCountCount)
 | |
| 		}
 | |
| 		if len(quotaSizeMap) != expectedQuotaSizeCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) quotaSizeCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(quotaSizeMap), expectedQuotaSizeCount)
 | |
| 		}
 | |
| 		if len(supportsQuotasMap) != expectedSupportsQuotasCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) supportsQuotasCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(supportsQuotasMap), expectedSupportsQuotasCount)
 | |
| 		}
 | |
| 		if len(backingDevMap) != expectedBackingDevCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) BackingDevCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(backingDevMap), expectedBackingDevCount)
 | |
| 		}
 | |
| 		if len(mountpointMap) != expectedMountpointCount {
 | |
| 			fail = true
 | |
| 			t.Errorf("Case %v (%s, %v) MountpointCount mismatch: got %v, expect %v", seq, testcase.path, enabled, len(mountpointMap), expectedMountpointCount)
 | |
| 		}
 | |
| 		if fail {
 | |
| 			logAllMaps(fmt.Sprintf("%v %s", seq, testcase.path))
 | |
| 		}
 | |
| 	}
 | |
| 	os.Remove(projectsFile)
 | |
| 	os.Remove(projidFile)
 | |
| }
 | |
| 
 | |
| func TestAddRemoveQuotasEnabled(t *testing.T) {
 | |
| 	testAddRemoveQuotas(t, true)
 | |
| }
 | |
| 
 | |
| func TestAddRemoveQuotasDisabled(t *testing.T) {
 | |
| 	testAddRemoveQuotas(t, false)
 | |
| }
 |