pkg/volume: Use QuotaMonitoring in UserNamespace
Enable LocalStorageCapacityIsolationFSQuotaMonitoring only when hostUsers in PodSpec is set to false. Modify unit tests and e2e tests to verify Signed-off-by: PannagaRamamanohara <pbhojara@redhat.com>
This commit is contained in:
		| @@ -386,6 +386,8 @@ func (m *UsernsManager) createUserNs(pod *v1.Pod) (userNs userNamespace, err err | |||||||
| func (m *UsernsManager) GetOrCreateUserNamespaceMappings(pod *v1.Pod, runtimeHandler string) (*runtimeapi.UserNamespace, error) { | func (m *UsernsManager) GetOrCreateUserNamespaceMappings(pod *v1.Pod, runtimeHandler string) (*runtimeapi.UserNamespace, error) { | ||||||
| 	featureEnabled := utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesSupport) | 	featureEnabled := utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesSupport) | ||||||
|  |  | ||||||
|  | 	// TODO: If the default value for hostUsers ever changes, change the default value of | ||||||
|  | 	// userNamespacesEnabled as well | ||||||
| 	if pod == nil || pod.Spec.HostUsers == nil { | 	if pod == nil || pod.Spec.HostUsers == nil { | ||||||
| 		// if the feature is enabled, specify to use the node mode... | 		// if the feature is enabled, specify to use the node mode... | ||||||
| 		if featureEnabled { | 		if featureEnabled { | ||||||
| @@ -512,3 +514,7 @@ func (m *UsernsManager) CleanupOrphanedPodUsernsAllocations(pods []*v1.Pod, runn | |||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func EnabledUserNamespacesSupport() bool { | ||||||
|  | 	return utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesSupport) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ import ( | |||||||
| 	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" | 	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" | ||||||
| 	"k8s.io/kubernetes/pkg/features" | 	"k8s.io/kubernetes/pkg/features" | ||||||
| 	"k8s.io/kubernetes/pkg/kubelet/cm" | 	"k8s.io/kubernetes/pkg/kubelet/cm" | ||||||
|  | 	usernamespacefeature "k8s.io/kubernetes/pkg/kubelet/userns" | ||||||
| 	"k8s.io/kubernetes/pkg/volume" | 	"k8s.io/kubernetes/pkg/volume" | ||||||
| 	volumeutil "k8s.io/kubernetes/pkg/volume/util" | 	volumeutil "k8s.io/kubernetes/pkg/volume/util" | ||||||
| 	"k8s.io/kubernetes/pkg/volume/util/fsquota" | 	"k8s.io/kubernetes/pkg/volume/util/fsquota" | ||||||
| @@ -294,12 +295,19 @@ func (ed *emptyDir) assignQuota(dir string, mounterSize *resource.Quantity) erro | |||||||
| 	if mounterSize != nil { | 	if mounterSize != nil { | ||||||
| 		// Deliberately shadow the outer use of err as noted | 		// Deliberately shadow the outer use of err as noted | ||||||
| 		// above. | 		// above. | ||||||
| 		hasQuotas, err := fsquota.SupportsQuotas(ed.mounter, dir) | 		// hostUsers field is interpreted as true by default, so a pod is by default | ||||||
|  | 		// not confined by a user namespace. | ||||||
|  | 		userNamespacesEnabled := false | ||||||
|  | 		if usernamespacefeature.EnabledUserNamespacesSupport() { | ||||||
|  | 			userNamespacesEnabled = ed.pod.Spec.HostUsers != nil && !*ed.pod.Spec.HostUsers | ||||||
|  | 		} | ||||||
|  | 		hasQuotas, err := fsquota.SupportsQuotas(ed.mounter, dir, userNamespacesEnabled) | ||||||
|  | 		klog.V(3).Infof("assignQuota called, hasQuotas = %t userNamespacesEnabled = %t", hasQuotas, userNamespacesEnabled) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			klog.V(3).Infof("Unable to check for quota support on %s: %s", dir, err.Error()) | 			klog.V(3).Infof("Unable to check for quota support on %s: %s", dir, err.Error()) | ||||||
| 		} else if hasQuotas { | 		} else if hasQuotas { | ||||||
| 			klog.V(4).Infof("emptydir trying to assign quota %v on %s", mounterSize, dir) | 			klog.V(3).Infof("emptydir trying to assign quota %v on %s", mounterSize, dir) | ||||||
| 			if err := fsquota.AssignQuota(ed.mounter, dir, ed.pod.UID, mounterSize); err != nil { | 			if err := fsquota.AssignQuota(ed.mounter, dir, ed.pod.UID, mounterSize, userNamespacesEnabled); err != nil { | ||||||
| 				klog.V(3).Infof("Set quota on %s failed %s", dir, err.Error()) | 				klog.V(3).Infof("Set quota on %s failed %s", dir, err.Error()) | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| @@ -514,7 +522,11 @@ func (ed *emptyDir) TearDownAt(dir string) error { | |||||||
| func (ed *emptyDir) teardownDefault(dir string) error { | func (ed *emptyDir) teardownDefault(dir string) error { | ||||||
| 	if utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolationFSQuotaMonitoring) { | 	if utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolationFSQuotaMonitoring) { | ||||||
| 		// Remove any quota | 		// Remove any quota | ||||||
| 		err := fsquota.ClearQuota(ed.mounter, dir) | 		userNamespacesEnabled := false | ||||||
|  | 		if usernamespacefeature.EnabledUserNamespacesSupport() { | ||||||
|  | 			userNamespacesEnabled = ed.pod.Spec.HostUsers != nil && !*ed.pod.Spec.HostUsers | ||||||
|  | 		} | ||||||
|  | 		err := fsquota.ClearQuota(ed.mounter, dir, userNamespacesEnabled) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			klog.Warningf("Warning: Failed to clear quota on %s: %v", dir, err) | 			klog.Warningf("Warning: Failed to clear quota on %s: %v", dir, err) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -225,11 +225,11 @@ func GetQuotaOnDir(m mount.Interface, path string) (common.QuotaID, error) { | |||||||
| 	return getApplier(path).GetQuotaOnDir(path) | 	return getApplier(path).GetQuotaOnDir(path) | ||||||
| } | } | ||||||
|  |  | ||||||
| func clearQuotaOnDir(m mount.Interface, path string) error { | func clearQuotaOnDir(m mount.Interface, path string, userNamespacesEnabled bool) error { | ||||||
| 	// Since we may be called without path being in the map, | 	// Since we may be called without path being in the map, | ||||||
| 	// we explicitly have to check in this case. | 	// we explicitly have to check in this case. | ||||||
| 	klog.V(4).Infof("clearQuotaOnDir %s", path) | 	klog.V(4).Infof("clearQuotaOnDir %s", path) | ||||||
| 	supportsQuotas, err := SupportsQuotas(m, path) | 	supportsQuotas, err := SupportsQuotas(m, path, userNamespacesEnabled) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		// Log-and-continue instead of returning an error for now | 		// Log-and-continue instead of returning an error for now | ||||||
| 		// due to unspecified backwards compatibility concerns (a subject to revise) | 		// due to unspecified backwards compatibility concerns (a subject to revise) | ||||||
| @@ -269,11 +269,17 @@ func clearQuotaOnDir(m mount.Interface, path string) error { | |||||||
| // don't cache the result because nothing will clean it up. | // don't cache the result because nothing will clean it up. | ||||||
| // However, do cache the device->applier map; the number of devices | // However, do cache the device->applier map; the number of devices | ||||||
| // is bounded. | // is bounded. | ||||||
| func SupportsQuotas(m mount.Interface, path string) (bool, error) { | // User namespaces prevent changes to project IDs on the filesystem, | ||||||
|  | // ensuring xfs-quota metrics' reliability; hence, userNamespacesEnabled is checked. | ||||||
|  | func SupportsQuotas(m mount.Interface, path string, userNamespacesEnabled bool) (bool, error) { | ||||||
| 	if !enabledQuotasForMonitoring() { | 	if !enabledQuotasForMonitoring() { | ||||||
| 		klog.V(3).Info("SupportsQuotas called, but quotas disabled") | 		klog.V(3).Info("SupportsQuotas called, but quotas disabled") | ||||||
| 		return false, nil | 		return false, nil | ||||||
| 	} | 	} | ||||||
|  | 	if !userNamespacesEnabled { | ||||||
|  | 		klog.V(3).Info("SupportQuotas called and LocalStorageCapacityIsolationFSQuotaMonitoring enabled, but pod is not in a user namespace") | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
| 	supportsQuotasLock.Lock() | 	supportsQuotasLock.Lock() | ||||||
| 	defer supportsQuotasLock.Unlock() | 	defer supportsQuotasLock.Unlock() | ||||||
| 	if supportsQuotas, ok := supportsQuotasMap[path]; ok { | 	if supportsQuotas, ok := supportsQuotasMap[path]; ok { | ||||||
| @@ -307,12 +313,12 @@ func SupportsQuotas(m mount.Interface, path string) (bool, error) { | |||||||
| // AssignQuota chooses the quota ID based on the pod UID and path. | // AssignQuota chooses the quota ID based on the pod UID and path. | ||||||
| // If the pod UID is identical to another one known, it may (but presently | // If the pod UID is identical to another one known, it may (but presently | ||||||
| // doesn't) choose the same quota ID as other volumes in the pod. | // doesn't) choose the same quota ID as other volumes in the pod. | ||||||
| func AssignQuota(m mount.Interface, path string, poduid types.UID, bytes *resource.Quantity) error { //nolint:staticcheck | func AssignQuota(m mount.Interface, path string, poduid types.UID, bytes *resource.Quantity, userNamespacesEnabled bool) error { //nolint:staticcheck | ||||||
| 	if bytes == nil { | 	if bytes == nil { | ||||||
| 		return fmt.Errorf("attempting to assign null quota to %s", path) | 		return fmt.Errorf("attempting to assign null quota to %s", path) | ||||||
| 	} | 	} | ||||||
| 	ibytes := bytes.Value() | 	ibytes := bytes.Value() | ||||||
| 	if ok, err := SupportsQuotas(m, path); !ok { | 	if ok, err := SupportsQuotas(m, path, userNamespacesEnabled); !ok { | ||||||
| 		return fmt.Errorf("quotas not supported on %s: %v", path, err) | 		return fmt.Errorf("quotas not supported on %s: %v", path, err) | ||||||
| 	} | 	} | ||||||
| 	quotaLock.Lock() | 	quotaLock.Lock() | ||||||
| @@ -410,7 +416,7 @@ func GetInodes(path string) (*resource.Quantity, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ClearQuota -- remove the quota assigned to a directory | // ClearQuota -- remove the quota assigned to a directory | ||||||
| func ClearQuota(m mount.Interface, path string) error { | func ClearQuota(m mount.Interface, path string, userNamespacesEnabled bool) error { | ||||||
| 	klog.V(3).Infof("ClearQuota %s", path) | 	klog.V(3).Infof("ClearQuota %s", path) | ||||||
| 	if !enabledQuotasForMonitoring() { | 	if !enabledQuotasForMonitoring() { | ||||||
| 		return fmt.Errorf("clearQuota called, but quotas disabled") | 		return fmt.Errorf("clearQuota called, but quotas disabled") | ||||||
| @@ -426,7 +432,7 @@ func ClearQuota(m mount.Interface, path string) error { | |||||||
| 		// be found, which needs to be cleaned up. | 		// be found, which needs to be cleaned up. | ||||||
| 		defer delete(supportsQuotasMap, path) | 		defer delete(supportsQuotasMap, path) | ||||||
| 		defer clearApplier(path) | 		defer clearApplier(path) | ||||||
| 		return clearQuotaOnDir(m, path) | 		return clearQuotaOnDir(m, path, userNamespacesEnabled) | ||||||
| 	} | 	} | ||||||
| 	_, ok = podQuotaMap[poduid] | 	_, ok = podQuotaMap[poduid] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| @@ -443,7 +449,7 @@ func ClearQuota(m mount.Interface, path string) error { | |||||||
| 	} | 	} | ||||||
| 	count, ok := podDirCountMap[poduid] | 	count, ok := podDirCountMap[poduid] | ||||||
| 	if count <= 1 || !ok { | 	if count <= 1 || !ok { | ||||||
| 		err = clearQuotaOnDir(m, path) | 		err = clearQuotaOnDir(m, path, userNamespacesEnabled) | ||||||
| 		// This error should be noted; we still need to clean up | 		// This error should be noted; we still need to clean up | ||||||
| 		// and otherwise handle in the same way. | 		// and otherwise handle in the same way. | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|   | |||||||
| @@ -366,19 +366,19 @@ func (v testVolumeQuota) GetInodes(_ string, _ common.QuotaID) (int64, error) { | |||||||
| 	return 1, nil | 	return 1, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func fakeSupportsQuotas(path string) (bool, error) { | func fakeSupportsQuotas(path string, userNamespacesEnabled bool) (bool, error) { | ||||||
| 	dummySetFSInfo(path) | 	dummySetFSInfo(path) | ||||||
| 	return SupportsQuotas(dummyQuotaTest(), path) | 	return SupportsQuotas(dummyQuotaTest(), path, userNamespacesEnabled) | ||||||
| } | } | ||||||
|  |  | ||||||
| func fakeAssignQuota(path string, poduid types.UID, bytes int64) error { | func fakeAssignQuota(path string, poduid types.UID, bytes int64, userNamespacesEnabled bool) error { | ||||||
| 	dummySetFSInfo(path) | 	dummySetFSInfo(path) | ||||||
| 	return AssignQuota(dummyQuotaTest(), path, poduid, resource.NewQuantity(bytes, resource.DecimalSI)) | 	return AssignQuota(dummyQuotaTest(), path, poduid, resource.NewQuantity(bytes, resource.DecimalSI), userNamespacesEnabled) | ||||||
| } | } | ||||||
|  |  | ||||||
| func fakeClearQuota(path string) error { | func fakeClearQuota(path string, userNamespacesEnabled bool) error { | ||||||
| 	dummySetFSInfo(path) | 	dummySetFSInfo(path) | ||||||
| 	return ClearQuota(dummyQuotaTest(), path) | 	return ClearQuota(dummyQuotaTest(), path, userNamespacesEnabled) | ||||||
| } | } | ||||||
|  |  | ||||||
| type quotaTestCase struct { | type quotaTestCase struct { | ||||||
| @@ -391,6 +391,7 @@ type quotaTestCase struct { | |||||||
| 	expectedProjid                   string | 	expectedProjid                   string | ||||||
| 	supportsQuota                    bool | 	supportsQuota                    bool | ||||||
| 	expectsSetQuota                  bool | 	expectsSetQuota                  bool | ||||||
|  | 	userNamespacesEnabled            bool | ||||||
| 	deltaExpectedPodQuotaCount       int | 	deltaExpectedPodQuotaCount       int | ||||||
| 	deltaExpectedDirQuotaCount       int | 	deltaExpectedDirQuotaCount       int | ||||||
| 	deltaExpectedQuotaPodCount       int | 	deltaExpectedQuotaPodCount       int | ||||||
| @@ -444,59 +445,64 @@ volume1048581:1048581 | |||||||
|  |  | ||||||
| var quotaTestCases = []quotaTestCase{ | var quotaTestCases = []quotaTestCase{ | ||||||
| 	{ | 	{ | ||||||
| 		"SupportsQuotaOnQuotaVolume", | 		"SupportsQuotaOnQuotaVolumeWithUserNamespace", | ||||||
| 		"/quota1/a", "", 1024, "Supports", "", "", | 		"/quota1/a", "", 1024, "Supports", "", "", | ||||||
| 		true, true, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, | 		true, true, true, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		"SupportsQuotaOnQuotaVolumeWithoutUserNamespace", | ||||||
|  | 		"/quota1/a", "", 1024, "Supports", "", "", | ||||||
|  | 		true, true, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"AssignQuotaFirstTime", | 		"AssignQuotaFirstTime", | ||||||
| 		"/quota1/a", "", 1024, "Set", projects1, projid1, | 		"/quota1/a", "", 1024, "Set", projects1, projid1, | ||||||
| 		true, true, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, | 		true, true, true, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"AssignQuotaFirstTime", | 		"AssignQuotaFirstTime", | ||||||
| 		"/quota1/b", "x", 1024, "Set", projects2, projid2, | 		"/quota1/b", "x", 1024, "Set", projects2, projid2, | ||||||
| 		true, true, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, | 		true, true, true, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"AssignQuotaFirstTime", | 		"AssignQuotaFirstTime", | ||||||
| 		"/quota2/b", "x", 1024, "Set", projects3, projid3, | 		"/quota2/b", "x", 1024, "Set", projects3, projid3, | ||||||
| 		true, true, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | 		true, true, true, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"AssignQuotaSecondTimeWithSameSize", | 		"AssignQuotaSecondTimeWithSameSize", | ||||||
| 		"/quota1/b", "x", 1024, "Set", projects3, projid3, | 		"/quota1/b", "x", 1024, "Set", projects3, projid3, | ||||||
| 		true, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 		true, true, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"AssignQuotaSecondTimeWithDifferentSize", | 		"AssignQuotaSecondTimeWithDifferentSize", | ||||||
| 		"/quota2/b", "x", 2048, "Set", projects3, projid3, | 		"/quota2/b", "x", 2048, "Set", projects3, projid3, | ||||||
| 		true, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 		true, false, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"ClearQuotaFirstTime", | 		"ClearQuotaFirstTime", | ||||||
| 		"/quota1/b", "", 1024, "Clear", projects4, projid4, | 		"/quota1/b", "", 1024, "Clear", projects4, projid4, | ||||||
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | 		true, true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"SupportsQuotaOnNonQuotaVolume", | 		"SupportsQuotaOnNonQuotaVolume", | ||||||
| 		"/noquota/a", "", 1024, "Supports", projects4, projid4, | 		"/noquota/a", "", 1024, "Supports", projects4, projid4, | ||||||
| 		false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 		false, false, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"ClearQuotaFirstTime", | 		"ClearQuotaFirstTime", | ||||||
| 		"/quota1/a", "", 1024, "Clear", projects5, projid5, | 		"/quota1/a", "", 1024, "Clear", projects5, projid5, | ||||||
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | 		true, true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"ClearQuotaSecondTime", | 		"ClearQuotaSecondTime", | ||||||
| 		"/quota1/a", "", 1024, "Clear", projects5, projid5, | 		"/quota1/a", "", 1024, "Clear", projects5, projid5, | ||||||
| 		true, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | 		true, false, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		"ClearQuotaFirstTime", | 		"ClearQuotaFirstTime", | ||||||
| 		"/quota2/b", "", 1024, "Clear", "", "", | 		"/quota2/b", "", 1024, "Clear", "", "", | ||||||
| 		true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | 		true, true, true, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -534,20 +540,21 @@ func runCaseEnabled(t *testing.T, testcase quotaTestCase, seq int) bool { | |||||||
| 	var err error | 	var err error | ||||||
| 	switch testcase.op { | 	switch testcase.op { | ||||||
| 	case "Supports": | 	case "Supports": | ||||||
| 		supports, err := fakeSupportsQuotas(testcase.path) | 		supports, err := fakeSupportsQuotas(testcase.path, testcase.userNamespacesEnabled) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fail = true | 			fail = true | ||||||
| 			t.Errorf("Case %v (%s, %s, %v) Got error in fakeSupportsQuotas: %v", seq, testcase.name, testcase.path, true, err) | 			t.Errorf("Case %v (%s, %s, %v) Got error in fakeSupportsQuotas: %v", seq, testcase.name, testcase.path, true, err) | ||||||
| 		} | 		} | ||||||
| 		if supports != testcase.supportsQuota { | 		expectedSupport := testcase.supportsQuota && testcase.userNamespacesEnabled | ||||||
|  | 		if supports != expectedSupport { | ||||||
| 			fail = true | 			fail = true | ||||||
| 			t.Errorf("Case %v (%s, %s, %v) fakeSupportsQuotas got %v, expect %v", seq, testcase.name, testcase.path, true, supports, testcase.supportsQuota) | 			t.Errorf("Case %v (%s, %s, userNamespacesEnabled: %v) fakeSupportsQuotas got %v, expect %v", seq, testcase.name, testcase.path, testcase.userNamespacesEnabled, supports, expectedSupport) | ||||||
| 		} | 		} | ||||||
| 		return fail | 		return fail | ||||||
| 	case "Set": | 	case "Set": | ||||||
| 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes) | 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes, testcase.userNamespacesEnabled) | ||||||
| 	case "Clear": | 	case "Clear": | ||||||
| 		err = fakeClearQuota(testcase.path) | 		err = fakeClearQuota(testcase.path, testcase.userNamespacesEnabled) | ||||||
| 	case "GetConsumption": | 	case "GetConsumption": | ||||||
| 		_, err = GetConsumption(testcase.path) | 		_, err = GetConsumption(testcase.path) | ||||||
| 	case "GetInodes": | 	case "GetInodes": | ||||||
| @@ -571,15 +578,15 @@ func runCaseDisabled(t *testing.T, testcase quotaTestCase, seq int) bool { | |||||||
| 	var supports bool | 	var supports bool | ||||||
| 	switch testcase.op { | 	switch testcase.op { | ||||||
| 	case "Supports": | 	case "Supports": | ||||||
| 		if supports, _ = fakeSupportsQuotas(testcase.path); supports { | 		if supports, _ = fakeSupportsQuotas(testcase.path, testcase.userNamespacesEnabled); supports { | ||||||
| 			t.Errorf("Case %v (%s, %s, %v) supports quotas but shouldn't", seq, testcase.name, testcase.path, false) | 			t.Errorf("Case %v (%s, %s, %v) supports quotas but shouldn't", seq, testcase.name, testcase.path, false) | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 		return false | 		return false | ||||||
| 	case "Set": | 	case "Set": | ||||||
| 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes) | 		err = fakeAssignQuota(testcase.path, testcase.poduid, testcase.bytes, testcase.userNamespacesEnabled) | ||||||
| 	case "Clear": | 	case "Clear": | ||||||
| 		err = fakeClearQuota(testcase.path) | 		err = fakeClearQuota(testcase.path, testcase.userNamespacesEnabled) | ||||||
| 	case "GetConsumption": | 	case "GetConsumption": | ||||||
| 		_, err = GetConsumption(testcase.path) | 		_, err = GetConsumption(testcase.path) | ||||||
| 	case "GetInodes": | 	case "GetInodes": | ||||||
|   | |||||||
| @@ -39,12 +39,12 @@ func GetQuotaOnDir(_ mount.Interface, _ string) (common.QuotaID, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // SupportsQuotas -- dummy implementation | // SupportsQuotas -- dummy implementation | ||||||
| func SupportsQuotas(_ mount.Interface, _ string) (bool, error) { | func SupportsQuotas(_ mount.Interface, _ string, _ bool) (bool, error) { | ||||||
| 	return false, errNotImplemented | 	return false, errNotImplemented | ||||||
| } | } | ||||||
|  |  | ||||||
| // AssignQuota -- dummy implementation | // AssignQuota -- dummy implementation | ||||||
| func AssignQuota(_ mount.Interface, _ string, _ types.UID, _ *resource.Quantity) error { | func AssignQuota(_ mount.Interface, _ string, _ types.UID, _ *resource.Quantity, _ bool) error { | ||||||
| 	return errNotImplemented | 	return errNotImplemented | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -59,6 +59,6 @@ func GetInodes(_ string) (*resource.Quantity, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ClearQuota -- dummy implementation | // ClearQuota -- dummy implementation | ||||||
| func ClearQuota(_ mount.Interface, _ string) error { | func ClearQuota(_ mount.Interface, _ string, _ bool) error { | ||||||
| 	return errNotImplemented | 	return errNotImplemented | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ const ( | |||||||
| 	LSCIQuotaFeature = features.LocalStorageCapacityIsolationFSQuotaMonitoring | 	LSCIQuotaFeature = features.LocalStorageCapacityIsolationFSQuotaMonitoring | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func runOneQuotaTest(f *framework.Framework, quotasRequested bool) { | func runOneQuotaTest(f *framework.Framework, quotasRequested bool, userNamespacesEnabled bool) { | ||||||
| 	evictionTestTimeout := 10 * time.Minute | 	evictionTestTimeout := 10 * time.Minute | ||||||
| 	sizeLimit := resource.MustParse("100Mi") | 	sizeLimit := resource.MustParse("100Mi") | ||||||
| 	useOverLimit := 101 /* Mb */ | 	useOverLimit := 101 /* Mb */ | ||||||
| @@ -63,7 +63,10 @@ func runOneQuotaTest(f *framework.Framework, quotasRequested bool) { | |||||||
| 			defer withFeatureGate(LSCIQuotaFeature, quotasRequested)() | 			defer withFeatureGate(LSCIQuotaFeature, quotasRequested)() | ||||||
| 			// TODO: remove hardcoded kubelet volume directory path | 			// TODO: remove hardcoded kubelet volume directory path | ||||||
| 			// framework.TestContext.KubeVolumeDir is currently not populated for node e2e | 			// framework.TestContext.KubeVolumeDir is currently not populated for node e2e | ||||||
| 			if quotasRequested && !supportsQuotas("/var/lib/kubelet") { | 			if !supportsUserNS(ctx, f) { | ||||||
|  | 				e2eskipper.Skipf("runtime does not support user namespaces") | ||||||
|  | 			} | ||||||
|  | 			if quotasRequested && !supportsQuotas("/var/lib/kubelet", userNamespacesEnabled) { | ||||||
| 				// No point in running this as a positive test if quotas are not | 				// No point in running this as a positive test if quotas are not | ||||||
| 				// enabled on the underlying filesystem. | 				// enabled on the underlying filesystem. | ||||||
| 				e2eskipper.Skipf("Cannot run LocalStorageCapacityIsolationFSQuotaMonitoring on filesystem without project quota enabled") | 				e2eskipper.Skipf("Cannot run LocalStorageCapacityIsolationFSQuotaMonitoring on filesystem without project quota enabled") | ||||||
| @@ -98,11 +101,12 @@ func runOneQuotaTest(f *framework.Framework, quotasRequested bool) { | |||||||
| // pod that creates a file, deletes it, and writes data to it.  If | // pod that creates a file, deletes it, and writes data to it.  If | ||||||
| // quotas are used to monitor, it will detect this deleted-but-in-use | // quotas are used to monitor, it will detect this deleted-but-in-use | ||||||
| // file; if du is used to monitor, it will not detect this. | // file; if du is used to monitor, it will not detect this. | ||||||
| var _ = SIGDescribe("LocalStorageCapacityIsolationFSQuotaMonitoring", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.LocalStorageCapacityIsolationQuota, nodefeature.LSCIQuotaMonitoring, func() { | var _ = SIGDescribe("LocalStorageCapacityIsolationFSQuotaMonitoring", framework.WithSlow(), framework.WithSerial(), framework.WithDisruptive(), feature.LocalStorageCapacityIsolationQuota, nodefeature.LSCIQuotaMonitoring, nodefeature.UserNamespacesSupport, feature.UserNamespacesSupport, func() { | ||||||
| 	f := framework.NewDefaultFramework("localstorage-quota-monitoring-test") | 	f := framework.NewDefaultFramework("localstorage-quota-monitoring-test") | ||||||
| 	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged | 	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged | ||||||
| 	runOneQuotaTest(f, true) | 	runOneQuotaTest(f, true, true) | ||||||
| 	runOneQuotaTest(f, false) | 	runOneQuotaTest(f, true, false) | ||||||
|  | 	runOneQuotaTest(f, false, true) | ||||||
| 	addAfterEachForCleaningUpPods(f) | 	addAfterEachForCleaningUpPods(f) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| @@ -152,7 +156,7 @@ func diskConcealingPod(name string, diskConsumedMB int, volumeSource *v1.VolumeS | |||||||
|  |  | ||||||
| // Don't bother returning an error; if something goes wrong, | // Don't bother returning an error; if something goes wrong, | ||||||
| // simply treat it as "no". | // simply treat it as "no". | ||||||
| func supportsQuotas(dir string) bool { | func supportsQuotas(dir string, userNamespacesEnabled bool) bool { | ||||||
| 	supportsQuota, err := fsquota.SupportsQuotas(mount.New(""), dir) | 	supportsQuota, err := fsquota.SupportsQuotas(mount.New(""), dir, userNamespacesEnabled) | ||||||
| 	return supportsQuota && err == nil | 	return supportsQuota && err == nil | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 PannagaRamamanohara
					PannagaRamamanohara