Add local storage to downwards API

This commit is contained in:
NickrenREN 2017-08-10 15:26:07 +08:00
parent acdf625e46
commit 194418986f
6 changed files with 85 additions and 3 deletions

View File

@ -88,10 +88,14 @@ func ExtractContainerResourceValue(fs *api.ResourceFieldSelector, container *api
return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor) return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
case "limits.memory": case "limits.memory":
return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor) return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
case "limits.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
case "requests.cpu": case "requests.cpu":
return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor) return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
case "requests.memory": case "requests.memory":
return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor) return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
case "requests.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
} }
return "", fmt.Errorf("unsupported container resource : %v", fs.Resource) return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
@ -110,3 +114,10 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value()))) m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil return strconv.FormatInt(m, 10), nil
} }
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
// ceiling of the value.
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil
}

View File

@ -1470,7 +1470,7 @@ type EnvVarSource struct {
// +optional // +optional
FieldRef *ObjectFieldSelector FieldRef *ObjectFieldSelector
// Selects a resource of the container: only resources limits and requests // Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. // (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
// +optional // +optional
ResourceFieldRef *ResourceFieldSelector ResourceFieldRef *ResourceFieldSelector
// Selects a key of a ConfigMap. // Selects a key of a ConfigMap.

View File

@ -153,10 +153,14 @@ func ExtractContainerResourceValue(fs *v1.ResourceFieldSelector, container *v1.C
return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor) return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
case "limits.memory": case "limits.memory":
return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor) return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
case "limits.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
case "requests.cpu": case "requests.cpu":
return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor) return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
case "requests.memory": case "requests.memory":
return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor) return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
case "requests.ephemeral-storage":
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
} }
return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource) return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
@ -176,6 +180,13 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
return strconv.FormatInt(m, 10), nil return strconv.FormatInt(m, 10), nil
} }
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
// ceiling of the value.
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
return strconv.FormatInt(m, 10), nil
}
// findContainerInPod finds a container by its name in the provided pod // findContainerInPod finds a container by its name in the provided pod
func findContainerInPod(pod *v1.Pod, containerName string) (*v1.Container, error) { func findContainerInPod(pod *v1.Pod, containerName string) (*v1.Container, error) {
for _, container := range pod.Spec.Containers { for _, container := range pod.Spec.Containers {

View File

@ -1644,7 +1644,7 @@ func ValidateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
} }
var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP") var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP")
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "requests.cpu", "requests.memory") var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "limits.ephemeral-storage", "requests.cpu", "requests.memory", "requests.ephemeral-storage")
func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList { func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
@ -1704,6 +1704,13 @@ func validateObjectFieldSelector(fs *api.ObjectFieldSelector, expressions *sets.
return allErrs return allErrs
} }
func fsResourceIsEphemeralStorage(resource string) bool {
if resource == "limits.ephemeral-storage" || resource == "requests.ephemeral-storage" {
return true
}
return false
}
func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expressions *sets.String, fldPath *field.Path, volume bool) field.ErrorList { func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expressions *sets.String, fldPath *field.Path, volume bool) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
@ -1713,6 +1720,8 @@ func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expre
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
} else if !expressions.Has(fs.Resource) { } else if !expressions.Has(fs.Resource) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List()))
} else if fsResourceIsEphemeralStorage(fs.Resource) && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
allErrs = append(allErrs, field.Forbidden(fldPath, "Containers' ephemeral storage requests/limits disabled by feature-gate for Downward API"))
} }
allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...) allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
return allErrs return allErrs
@ -1773,6 +1782,7 @@ func validateSecretEnvSource(secretSource *api.SecretEnvSource, fldPath *field.P
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1") var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei") var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
var validContainerResourceDivisorForEphemeralStorage = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList { func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
@ -1789,6 +1799,10 @@ func validateContainerResourceDivisor(rName string, divisor resource.Quantity, f
if !validContainerResourceDivisorForMemory.Has(divisor.String()) { if !validContainerResourceDivisorForMemory.Has(divisor.String()) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource")) allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource"))
} }
case "limits.ephemeral-storage", "requests.ephemeral-storage":
if !validContainerResourceDivisorForEphemeralStorage.Has(divisor.String()) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the local ephemeral storage resource"))
}
} }
return allErrs return allErrs
} }

View File

@ -2807,6 +2807,52 @@ func TestValidatePorts(t *testing.T) {
} }
} }
func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
testCases := []api.EnvVar{
{
Name: "ephemeral-storage-limits",
ValueFrom: &api.EnvVarSource{
ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "limits.ephemeral-storage",
},
},
},
{
Name: "ephemeral-storage-requests",
ValueFrom: &api.EnvVarSource{
ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "requests.ephemeral-storage",
},
},
},
}
// 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
}
for _, testCase := range testCases {
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success, got: %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
}
for _, testCase := range testCases {
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %v", testCase.Name)
}
}
}
func TestValidateEnv(t *testing.T) { func TestValidateEnv(t *testing.T) {
successCase := []api.EnvVar{ successCase := []api.EnvVar{
{Name: "abc", Value: "value"}, {Name: "abc", Value: "value"},

View File

@ -1573,7 +1573,7 @@ type EnvVarSource struct {
// +optional // +optional
FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty" protobuf:"bytes,1,opt,name=fieldRef"` FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty" protobuf:"bytes,1,opt,name=fieldRef"`
// Selects a resource of the container: only resources limits and requests // Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. // (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
// +optional // +optional
ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty" protobuf:"bytes,2,opt,name=resourceFieldRef"` ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty" protobuf:"bytes,2,opt,name=resourceFieldRef"`
// Selects a key of a ConfigMap. // Selects a key of a ConfigMap.