mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 15:37:24 +00:00
Downward API implementation for resources limits and requests
This commit is contained in:
@@ -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{}
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in New Issue
Block a user