mirror of https://github.com/k3s-io/k3s
Merge pull request #49610 from NickrenREN/local-isolation
Automatic merge from submit-queue (batch tested with PRs 50932, 49610, 51312, 51415, 50705) Add local storage support in Quota Add local storage(scratch, overlay) support in quota **Release note**: ```release-note Add local ephemeral storage support to Quota ``` /cc @ddysher @jingxu97pull/6/head
commit
fe365b88e3
|
@ -170,11 +170,14 @@ func IsStandardLimitRangeType(str string) bool {
|
|||
var standardQuotaResources = sets.NewString(
|
||||
string(api.ResourceCPU),
|
||||
string(api.ResourceMemory),
|
||||
string(api.ResourceEphemeralStorage),
|
||||
string(api.ResourceRequestsCPU),
|
||||
string(api.ResourceRequestsMemory),
|
||||
string(api.ResourceRequestsStorage),
|
||||
string(api.ResourceRequestsEphemeralStorage),
|
||||
string(api.ResourceLimitsCPU),
|
||||
string(api.ResourceLimitsMemory),
|
||||
string(api.ResourceLimitsEphemeralStorage),
|
||||
string(api.ResourcePods),
|
||||
string(api.ResourceQuotas),
|
||||
string(api.ResourceServices),
|
||||
|
|
|
@ -3493,10 +3493,23 @@ func validateContainerResourceName(value string, fldPath *field.Path) field.Erro
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// isLocalStorageResource checks whether the resource is local ephemeral storage
|
||||
func isLocalStorageResource(name string) bool {
|
||||
if name == string(api.ResourceEphemeralStorage) || name == string(api.ResourceRequestsEphemeralStorage) ||
|
||||
name == string(api.ResourceLimitsEphemeralStorage) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Validate resource names that can go in a resource quota
|
||||
// Refer to docs/design/resources.md for more details.
|
||||
func ValidateResourceQuotaResourceName(value string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := validateResourceName(value, fldPath)
|
||||
if isLocalStorageResource(value) && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
|
||||
return append(allErrs, field.Forbidden(fldPath, "ResourceEphemeralStorage field disabled by feature-gate for ResourceQuota"))
|
||||
}
|
||||
if len(strings.Split(value, "/")) == 1 {
|
||||
if !helper.IsStandardQuotaResourceName(value) {
|
||||
return append(allErrs, field.Invalid(fldPath, value, isInvalidQuotaResource))
|
||||
|
|
|
@ -2709,6 +2709,62 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestValidateResourceQuotaWithAlphaLocalStorageCapacityIsolation(t *testing.T) {
|
||||
spec := api.ResourceQuotaSpec{
|
||||
Hard: api.ResourceList{
|
||||
api.ResourceCPU: resource.MustParse("100"),
|
||||
api.ResourceMemory: resource.MustParse("10000"),
|
||||
api.ResourceRequestsCPU: resource.MustParse("100"),
|
||||
api.ResourceRequestsMemory: resource.MustParse("10000"),
|
||||
api.ResourceLimitsCPU: resource.MustParse("100"),
|
||||
api.ResourceLimitsMemory: resource.MustParse("10000"),
|
||||
api.ResourcePods: resource.MustParse("10"),
|
||||
api.ResourceServices: resource.MustParse("0"),
|
||||
api.ResourceReplicationControllers: resource.MustParse("10"),
|
||||
api.ResourceQuotas: resource.MustParse("10"),
|
||||
api.ResourceConfigMaps: resource.MustParse("10"),
|
||||
api.ResourceSecrets: resource.MustParse("10"),
|
||||
api.ResourceEphemeralStorage: resource.MustParse("10000"),
|
||||
api.ResourceRequestsEphemeralStorage: resource.MustParse("10000"),
|
||||
api.ResourceLimitsEphemeralStorage: resource.MustParse("10000"),
|
||||
},
|
||||
}
|
||||
resourceQuota := &api.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "abc",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
// Enable alpha feature LocalStorageCapacityIsolation
|
||||
err := utilfeature.DefaultFeatureGate.Set("LocalStorageCapacityIsolation=true")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to enable feature gate for LocalStorageCapacityIsolation: %v", err)
|
||||
return
|
||||
}
|
||||
if errs := ValidateResourceQuota(resourceQuota); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
|
||||
// Disable alpha feature LocalStorageCapacityIsolation
|
||||
err = utilfeature.DefaultFeatureGate.Set("LocalStorageCapacityIsolation=false")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to disable feature gate for LocalStorageCapacityIsolation: %v", err)
|
||||
return
|
||||
}
|
||||
errs := ValidateResourceQuota(resourceQuota)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", resourceQuota.Name)
|
||||
}
|
||||
expectedErrMes := "ResourceEphemeralStorage field disabled by feature-gate for ResourceQuota"
|
||||
for i := range errs {
|
||||
if !strings.Contains(errs[i].Detail, expectedErrMes) {
|
||||
t.Errorf("[%s]: expected error detail either empty or %s, got %s", resourceQuota.Name, expectedErrMes, errs[i].Detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePorts(t *testing.T) {
|
||||
successCase := []api.ContainerPort{
|
||||
{Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
|
||||
|
|
|
@ -43,10 +43,13 @@ import (
|
|||
var podResources = []api.ResourceName{
|
||||
api.ResourceCPU,
|
||||
api.ResourceMemory,
|
||||
api.ResourceEphemeralStorage,
|
||||
api.ResourceRequestsCPU,
|
||||
api.ResourceRequestsMemory,
|
||||
api.ResourceRequestsEphemeralStorage,
|
||||
api.ResourceLimitsCPU,
|
||||
api.ResourceLimitsMemory,
|
||||
api.ResourceLimitsEphemeralStorage,
|
||||
api.ResourcePods,
|
||||
}
|
||||
|
||||
|
@ -201,6 +204,13 @@ func podUsageHelper(requests api.ResourceList, limits api.ResourceList) api.Reso
|
|||
if limit, found := limits[api.ResourceMemory]; found {
|
||||
result[api.ResourceLimitsMemory] = limit
|
||||
}
|
||||
if request, found := requests[api.ResourceEphemeralStorage]; found {
|
||||
result[api.ResourceEphemeralStorage] = request
|
||||
result[api.ResourceRequestsEphemeralStorage] = request
|
||||
}
|
||||
if limit, found := limits[api.ResourceEphemeralStorage]; found {
|
||||
result[api.ResourceLimitsEphemeralStorage] = limit
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,24 @@ func TestPodEvaluatorUsage(t *testing.T) {
|
|||
api.ResourceMemory: resource.MustParse("1m"),
|
||||
},
|
||||
},
|
||||
"init container local ephemeral storage": {
|
||||
pod: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
InitContainers: []api.Container{{
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("32Mi")},
|
||||
Limits: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("64Mi")},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
usage: api.ResourceList{
|
||||
api.ResourceEphemeralStorage: resource.MustParse("32Mi"),
|
||||
api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"),
|
||||
api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"),
|
||||
api.ResourcePods: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
"container CPU": {
|
||||
pod: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
|
@ -178,6 +196,24 @@ func TestPodEvaluatorUsage(t *testing.T) {
|
|||
api.ResourceMemory: resource.MustParse("1m"),
|
||||
},
|
||||
},
|
||||
"container local ephemeral storage": {
|
||||
pod: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("32Mi")},
|
||||
Limits: api.ResourceList{api.ResourceEphemeralStorage: resource.MustParse("64Mi")},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
usage: api.ResourceList{
|
||||
api.ResourceEphemeralStorage: resource.MustParse("32Mi"),
|
||||
api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"),
|
||||
api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"),
|
||||
api.ResourcePods: resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
"init container maximums override sum of containers": {
|
||||
pod: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
|
|
Loading…
Reference in New Issue