Merge pull request #66843 from verult/validate-allowedtopologies

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Added set and map structural validation for AllowedTopologies

**What this PR does / why we need it**: Adding structural validation to AllowedTopologies field in StorageClass.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #66184

**Release note**:

```release-note
AllowedTopologies field inside StorageClass is now validated against set and map semantics. Specifically, there cannot be duplicate TopologySelectorTerms, MatchLabelExpressions keys, and TopologySelectorLabelRequirement Values.
```
This commit is contained in:
Kubernetes Submit Queue
2018-08-27 15:09:52 -07:00
committed by GitHub
4 changed files with 276 additions and 10 deletions

View File

@@ -3105,30 +3105,55 @@ func ValidateNodeSelector(nodeSelector *core.NodeSelector, fldPath *field.Path)
return allErrs
}
// validateTopologySelectorLabelRequirement tests that the specified TopologySelectorLabelRequirement fields has valid data
func validateTopologySelectorLabelRequirement(rq core.TopologySelectorLabelRequirement, fldPath *field.Path) field.ErrorList {
// validateTopologySelectorLabelRequirement tests that the specified TopologySelectorLabelRequirement fields has valid data,
// and constructs a set containing all of its Values.
func validateTopologySelectorLabelRequirement(rq core.TopologySelectorLabelRequirement, fldPath *field.Path) (sets.String, field.ErrorList) {
allErrs := field.ErrorList{}
valueSet := make(sets.String)
valuesPath := fldPath.Child("values")
if len(rq.Values) == 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "must specify as least one value"))
allErrs = append(allErrs, field.Required(valuesPath, ""))
}
// Validate set property of Values field
for i, value := range rq.Values {
if valueSet.Has(value) {
allErrs = append(allErrs, field.Duplicate(valuesPath.Index(i), value))
}
valueSet.Insert(value)
}
allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(rq.Key, fldPath.Child("key"))...)
return allErrs
return valueSet, allErrs
}
// ValidateTopologySelectorTerm tests that the specified topology selector term has valid data
func ValidateTopologySelectorTerm(term core.TopologySelectorTerm, fldPath *field.Path) field.ErrorList {
// ValidateTopologySelectorTerm tests that the specified topology selector term has valid data,
// and constructs a map representing the term in raw form.
func ValidateTopologySelectorTerm(term core.TopologySelectorTerm, fldPath *field.Path) (map[string]sets.String, field.ErrorList) {
allErrs := field.ErrorList{}
exprMap := make(map[string]sets.String)
exprPath := fldPath.Child("matchLabelExpressions")
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicProvisioningScheduling) {
// Allow empty MatchLabelExpressions, in case this field becomes optional in the future.
for i, req := range term.MatchLabelExpressions {
allErrs = append(allErrs, validateTopologySelectorLabelRequirement(req, fldPath.Child("matchLabelExpressions").Index(i))...)
idxPath := exprPath.Index(i)
valueSet, exprErrs := validateTopologySelectorLabelRequirement(req, idxPath)
allErrs = append(allErrs, exprErrs...)
// Validate no duplicate keys exist.
if _, exists := exprMap[req.Key]; exists {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("key"), req.Key))
}
exprMap[req.Key] = valueSet
}
} else if len(term.MatchLabelExpressions) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath, "field is disabled by feature-gate DynamicProvisioningScheduling"))
}
return allErrs
return exprMap, allErrs
}
// ValidateAvoidPodsInNodeAnnotations tests that the serialized AvoidPods in Node.Annotations has valid data