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  @jingxu97
pull/6/head
Kubernetes Submit Queue 2017-08-29 01:17:34 -07:00 committed by GitHub
commit fe365b88e3
5 changed files with 118 additions and 0 deletions

View File

@ -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),

View File

@ -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))

View File

@ -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"},

View File

@ -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
}

View File

@ -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{