Downward API implementation for resources limits and requests

This commit is contained in:
Avesh Agarwal
2016-05-23 18:08:22 -04:00
parent 5303794ef0
commit 1931931494
27 changed files with 2686 additions and 725 deletions

View File

@@ -702,7 +702,16 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
}
allErrs = append(allErrs, validateVolumeSourcePath(downwardAPIVolumeFile.Path, fldPath.Child("path"))...)
allErrs = append(allErrs, validateObjectFieldSelector(&downwardAPIVolumeFile.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
if downwardAPIVolumeFile.FieldRef != nil {
allErrs = append(allErrs, validateObjectFieldSelector(downwardAPIVolumeFile.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
if downwardAPIVolumeFile.ResourceFieldRef != nil {
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
}
} else if downwardAPIVolumeFile.ResourceFieldRef != nil {
allErrs = append(allErrs, validateContainerResourceFieldSelector(downwardAPIVolumeFile.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
} else {
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
}
}
return allErrs
}
@@ -1048,6 +1057,7 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
}
var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "status.podIP")
var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "requests.cpu", "requests.memory")
func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@@ -1062,6 +1072,10 @@ func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList
numSources++
allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validFieldPathExpressionsEnv, fldPath.Child("fieldRef"))...)
}
if ev.ValueFrom.ResourceFieldRef != nil {
numSources++
allErrs = append(allErrs, validateContainerResourceFieldSelector(ev.ValueFrom.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), false)...)
}
if ev.ValueFrom.ConfigMapKeyRef != nil {
numSources++
allErrs = append(allErrs, validateConfigMapKeySelector(ev.ValueFrom.ConfigMapKeyRef, fldPath.Child("configMapKeyRef"))...)
@@ -1101,6 +1115,42 @@ func validateObjectFieldSelector(fs *api.ObjectFieldSelector, expressions *sets.
return allErrs
}
func validateContainerResourceFieldSelector(fs *api.ResourceFieldSelector, expressions *sets.String, fldPath *field.Path, volume bool) field.ErrorList {
allErrs := field.ErrorList{}
if volume && len(fs.ContainerName) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("containerName"), ""))
} else if len(fs.Resource) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
} else if !expressions.Has(fs.Resource) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, expressions.List()))
}
allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
return allErrs
}
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
var validContainerResourceDivisorForMemory = sets.NewString("1m", "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 {
allErrs := field.ErrorList{}
unsetDivisor := resource.Quantity{}
if unsetDivisor.Cmp(divisor) == 0 {
return allErrs
}
switch rName {
case "limits.cpu", "requests.cpu":
if !validContainerResourceDivisorForCPU.Has(divisor.String()) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, fmt.Sprintf("only divisor's values 1m and 1 are supported with the cpu resource")))
}
case "limits.memory", "requests.memory":
if !validContainerResourceDivisorForMemory.Has(divisor.String()) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, fmt.Sprintf("only divisor's values 1m, 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource")))
}
}
return allErrs
}
func validateConfigMapKeySelector(s *api.ConfigMapKeySelector, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

View File

@@ -656,30 +656,42 @@ func TestValidateVolumes(t *testing.T) {
{Name: "cinder", VolumeSource: api.VolumeSource{Cinder: &api.CinderVolumeSource{VolumeID: "29ea5088-4f60-4757-962e-dba678767887", FSType: "ext4", ReadOnly: false}}},
{Name: "cephfs", VolumeSource: api.VolumeSource{CephFS: &api.CephFSVolumeSource{Monitors: []string{"foo"}}}},
{Name: "downwardapi", VolumeSource: api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{
{Path: "labels", FieldRef: api.ObjectFieldSelector{
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}},
{Path: "annotations", FieldRef: api.ObjectFieldSelector{
{Path: "annotations", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.annotations"}},
{Path: "namespace", FieldRef: api.ObjectFieldSelector{
{Path: "namespace", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.namespace"}},
{Path: "name", FieldRef: api.ObjectFieldSelector{
{Path: "name", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.name"}},
{Path: "path/withslash/andslash", FieldRef: api.ObjectFieldSelector{
{Path: "path/withslash/andslash", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}},
{Path: "path/./withdot", FieldRef: api.ObjectFieldSelector{
{Path: "path/./withdot", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}},
{Path: "path/with..dot", FieldRef: api.ObjectFieldSelector{
{Path: "path/with..dot", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}},
{Path: "second-level-dirent-can-have/..dot", FieldRef: api.ObjectFieldSelector{
{Path: "second-level-dirent-can-have/..dot", FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}},
{Path: "cpu_limit", ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "limits.cpu"}},
{Path: "cpu_request", ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "requests.cpu"}},
{Path: "memory_limit", ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "limits.memory"}},
{Path: "memory_request", ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "requests.memory"}},
}}}},
{Name: "fc", VolumeSource: api.VolumeSource{FC: &api.FCVolumeSource{TargetWWNs: []string{"some_wwn"}, Lun: &lun, FSType: "ext4", ReadOnly: false}}},
{Name: "flexvolume", VolumeSource: api.VolumeSource{FlexVolume: &api.FlexVolumeSource{Driver: "kubernetes.io/blue", FSType: "ext4"}}},
@@ -705,30 +717,38 @@ func TestValidateVolumes(t *testing.T) {
containsDots := api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "foo", Directory: "dots/../bar"}}
absPath := api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{Repository: "foo", Directory: "/abstarget"}}
emptyPathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "",
FieldRef: api.ObjectFieldSelector{
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}}},
}}
absolutePathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "/absolutepath",
FieldRef: api.ObjectFieldSelector{
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}}},
}}
dotDotInPath := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "../../passwd",
FieldRef: api.ObjectFieldSelector{
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}}},
}}
dotDotPathName := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "..badFileName",
FieldRef: api.ObjectFieldSelector{
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}}},
}}
dotDotFirstLevelDirent := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "..badDirName/goodFileName",
FieldRef: api.ObjectFieldSelector{
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"}}},
}}
fieldRefandResourceFieldRef := api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{Items: []api.DownwardAPIVolumeFile{{Path: "test",
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels"},
ResourceFieldRef: &api.ResourceFieldSelector{
ContainerName: "test-container",
Resource: "requests.memory"}}},
}}
zeroWWN := api.VolumeSource{FC: &api.FCVolumeSource{TargetWWNs: []string{}, Lun: &lun, FSType: "ext4", ReadOnly: false}}
emptyLun := api.VolumeSource{FC: &api.FCVolumeSource{TargetWWNs: []string{"wwn"}, Lun: nil, FSType: "ext4", ReadOnly: false}}
slashInName := api.VolumeSource{Flocker: &api.FlockerVolumeSource{DatasetName: "foo/bar"}}
@@ -865,6 +885,11 @@ func TestValidateVolumes(t *testing.T) {
field.ErrorTypeRequired,
"azureFile.shareName", "",
},
"fieldRef and ResourceFieldRef together": {
[]api.Volume{{Name: "testvolume", VolumeSource: fieldRefandResourceFieldRef}},
field.ErrorTypeInvalid,
"downwardAPI", "fieldRef and resourceFieldRef can not be specified simultaneously",
},
}
for k, v := range errorCases {
_, errs := validateVolumes(v.V, field.NewPath("field"))