mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
adding validation
validation test validation test validation adding validation for fs/pl status validation validation validation replace < with <= validation test
This commit is contained in:
parent
38171e7fba
commit
c2d081eaf9
342
pkg/apis/flowcontrol/validation/validation.go
Normal file
342
pkg/apis/flowcontrol/validation/validation.go
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/api/validation"
|
||||
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/util/shufflesharding"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
)
|
||||
|
||||
// ValidateFlowSchemaName validates name for flow-schema.
|
||||
var ValidateFlowSchemaName = apimachineryvalidation.NameIsDNSSubdomain
|
||||
|
||||
// ValidatePriorityLevelConfigurationName validates name for priority-level-configuration.
|
||||
var ValidatePriorityLevelConfigurationName = apimachineryvalidation.NameIsDNSSubdomain
|
||||
|
||||
var supportedDistinguisherMethods = sets.NewString(
|
||||
string(flowcontrol.FlowDistinguisherMethodByNamespaceType),
|
||||
string(flowcontrol.FlowDistinguisherMethodByUserType),
|
||||
)
|
||||
|
||||
var priorityLevelConfigurationQueuingMaxQueues int32 = 10 * 1000 * 1000 // 10^7
|
||||
|
||||
var supportedVerbs = sets.NewString(
|
||||
"get",
|
||||
"list",
|
||||
"create",
|
||||
"update",
|
||||
"delete",
|
||||
"deletecollection",
|
||||
"patch",
|
||||
"watch",
|
||||
"proxy",
|
||||
)
|
||||
|
||||
var supportedSubjectKinds = sets.NewString(
|
||||
string(flowcontrol.SubjectKindServiceAccount),
|
||||
string(flowcontrol.SubjectKindGroup),
|
||||
string(flowcontrol.SubjectKindUser),
|
||||
)
|
||||
|
||||
var supportedQueuingType = sets.NewString(
|
||||
string(flowcontrol.PriorityLevelQueuingTypeQueueing),
|
||||
string(flowcontrol.PriorityLevelQueuingTypeExempt),
|
||||
)
|
||||
|
||||
// ValidateFlowSchema validates the content of flow-schema
|
||||
func ValidateFlowSchema(fs *flowcontrol.FlowSchema) field.ErrorList {
|
||||
allErrs := apivalidation.ValidateObjectMeta(&fs.ObjectMeta, false, ValidateFlowSchemaName, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateFlowSchemaSpec(&fs.Spec, field.NewPath("spec"))...)
|
||||
allErrs = append(allErrs, ValidateFlowSchemaStatus(&fs.Status, field.NewPath("status"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaSpec validates the content of flow-schema's spec
|
||||
func ValidateFlowSchemaSpec(spec *flowcontrol.FlowSchemaSpec, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if spec.MatchingPrecedence <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("matchingPrecedence"), spec.MatchingPrecedence, "must be positive value"))
|
||||
}
|
||||
if spec.DistinguisherMethod != nil {
|
||||
if !supportedDistinguisherMethods.Has(string(spec.DistinguisherMethod.Type)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("distinguisherMethod").Child("type"), spec.DistinguisherMethod, supportedDistinguisherMethods.List()))
|
||||
}
|
||||
}
|
||||
if len(spec.PriorityLevelConfiguration.Name) > 0 {
|
||||
for _, msg := range ValidatePriorityLevelConfigurationName(spec.PriorityLevelConfiguration.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityLevelConfiguration").Child("name"), spec.PriorityLevelConfiguration.Name, msg))
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("priorityLevelConfiguration").Child("name"), "must reference a priority level"))
|
||||
}
|
||||
for i, rule := range spec.Rules {
|
||||
allErrs = append(allErrs, ValidateFlowSchemaPolicyRulesWithSubjects(&rule, fldPath.Child("rules").Index(i))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaPolicyRulesWithSubjects validates policy-rule-with-subjects object.
|
||||
func ValidateFlowSchemaPolicyRulesWithSubjects(rule *flowcontrol.PolicyRulesWithSubjects, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(rule.Subjects) > 0 {
|
||||
for i, subject := range rule.Subjects {
|
||||
allErrs = append(allErrs, ValidateFlowSchemaSubject(&subject, fldPath.Child("subjects").Index(i))...)
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("subjects"), "subjects must contain at least one value"))
|
||||
}
|
||||
|
||||
if len(rule.ResourceRules) == 0 && len(rule.NonResourceRules) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "at least one of resourceRules and nonResourceRules has to be non-empty"))
|
||||
}
|
||||
for i, resourceRule := range rule.ResourceRules {
|
||||
allErrs = append(allErrs, ValidateFlowSchemaResourcePolicyRule(&resourceRule, fldPath.Child("resourceRules").Index(i))...)
|
||||
}
|
||||
for i, nonResourceRule := range rule.NonResourceRules {
|
||||
allErrs = append(allErrs, ValidateFlowSchemaNonResourcePolicyRule(&nonResourceRule, fldPath.Child("nonResourceRules").Index(i))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaSubject validates flow-schema's subject object.
|
||||
func ValidateFlowSchemaSubject(subject *flowcontrol.Subject, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
switch subject.Kind {
|
||||
case flowcontrol.SubjectKindServiceAccount:
|
||||
allErrs = append(allErrs, ValidateServiceAccountSubject(subject.ServiceAccount, fldPath.Child("serviceAccount"))...)
|
||||
case flowcontrol.SubjectKindUser:
|
||||
allErrs = append(allErrs, ValidateUserSubject(subject.User, fldPath.Child("user"))...)
|
||||
case flowcontrol.SubjectKindGroup:
|
||||
allErrs = append(allErrs, ValidateGroupSubject(subject.Group, fldPath.Child("group"))...)
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, supportedSubjectKinds.List()))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateServiceAccountSubject validates subject of "ServiceAccount" kind
|
||||
func ValidateServiceAccountSubject(subject *flowcontrol.ServiceAccountSubject, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(subject.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
} else if subject.Name != flowcontrol.NameAll {
|
||||
for _, msg := range validation.ValidateServiceAccountName(subject.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
|
||||
}
|
||||
}
|
||||
|
||||
if len(subject.Namespace) > 0 {
|
||||
for _, msg := range apimachineryvalidation.ValidateNamespaceName(subject.Namespace, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), subject.Namespace, msg))
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "must specify namespace for service account"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateUserSubject validates subject of "User" kind
|
||||
func ValidateUserSubject(subject *flowcontrol.UserSubject, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(subject.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateGroupSubject validates subject of "Group" kind
|
||||
func ValidateGroupSubject(subject *flowcontrol.GroupSubject, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(subject.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaNonResourcePolicyRule validates non-resource policy-rule in the flow-schema.
|
||||
func ValidateFlowSchemaNonResourcePolicyRule(rule *flowcontrol.NonResourcePolicyRule, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
|
||||
if len(rule.Verbs) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
|
||||
} else if hasWildcard(rule.Verbs) {
|
||||
if len(rule.Verbs) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
|
||||
}
|
||||
} else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
|
||||
// only supported verbs are allowed
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
|
||||
}
|
||||
|
||||
if len(rule.NonResourceURLs) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("nonResourceURLs"), "nonResourceURLs must contain at least one value"))
|
||||
} else if len(rule.NonResourceURLs) > 1 && hasWildcard(rule.NonResourceURLs) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "if '*' is present, must not specify other non-resource URLs"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaResourcePolicyRule validates resource policy-rule in the flow-schema.
|
||||
func ValidateFlowSchemaResourcePolicyRule(rule *flowcontrol.ResourcePolicyRule, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
|
||||
if len(rule.Verbs) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value"))
|
||||
} else if hasWildcard(rule.Verbs) {
|
||||
if len(rule.Verbs) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
|
||||
}
|
||||
} else if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
|
||||
// only supported verbs are allowed
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
|
||||
}
|
||||
|
||||
if len(rule.APIGroups) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
|
||||
} else if len(rule.APIGroups) > 1 && hasWildcard(rule.APIGroups) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroups"), rule.APIGroups, "if '*' is present, must not specify other api groups"))
|
||||
}
|
||||
|
||||
if len(rule.Resources) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
|
||||
} else if len(rule.Resources) > 1 && hasWildcard(rule.Resources) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("resources"), rule.Resources, "if '*' is present, must not specify other resources"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaStatus validates status for the flow-schema.
|
||||
func ValidateFlowSchemaStatus(status *flowcontrol.FlowSchemaStatus, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
keys := sets.NewString()
|
||||
for i, condition := range status.Conditions {
|
||||
if keys.Has(string(condition.Type)) {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
|
||||
}
|
||||
keys.Insert(string(condition.Type))
|
||||
allErrs = append(allErrs, ValidateFlowSchemaCondition(&condition, fldPath.Child("conditions").Index(i))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateFlowSchemaCondition validates condition in the flow-schema's status.
|
||||
func ValidateFlowSchemaCondition(condition *flowcontrol.FlowSchemaCondition, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(condition.Type) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePriorityLevelConfiguration validates priority-level-configuration.
|
||||
func ValidatePriorityLevelConfiguration(pl *flowcontrol.PriorityLevelConfiguration) field.ErrorList {
|
||||
allErrs := apivalidation.ValidateObjectMeta(&pl.ObjectMeta, false, ValidatePriorityLevelConfigurationName, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidatePriorityLevelConfigurationSpec(&pl.Spec, pl.Name, field.NewPath("spec"))...)
|
||||
allErrs = append(allErrs, ValidatePriorityLevelConfigurationStatus(&pl.Status, field.NewPath("status"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePriorityLevelConfigurationSpec validates priority-level-configuration's spec.
|
||||
func ValidatePriorityLevelConfigurationSpec(spec *flowcontrol.PriorityLevelConfigurationSpec, name string, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
switch spec.Type {
|
||||
case flowcontrol.PriorityLevelQueuingTypeExempt:
|
||||
if spec.Queuing != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("queuing"), "must be nil if the type is not Queuing"))
|
||||
}
|
||||
case flowcontrol.PriorityLevelQueuingTypeQueueing:
|
||||
if spec.Queuing == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("queuing"), "must not be empty"))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidatePriorityLevelQueuingConfiguration(spec.Queuing, fldPath.Child("queuing"))...)
|
||||
}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("type"), spec.Type, supportedQueuingType.List()))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePriorityLevelQueuingConfiguration validates queuing-configuration for a priority-level
|
||||
func ValidatePriorityLevelQueuingConfiguration(queuing *flowcontrol.QueuingConfiguration, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if queuing.AssuredConcurrencyShares <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("assuredConcurrencyShares"), queuing.AssuredConcurrencyShares, "must be positive"))
|
||||
}
|
||||
if queuing.QueueLengthLimit <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queueLengthLimit"), queuing.QueueLengthLimit, "must be positive"))
|
||||
}
|
||||
|
||||
// validate input arguments for shuffle-sharding
|
||||
if queuing.Queues <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues, "must be positive"))
|
||||
} else if queuing.Queues > priorityLevelConfigurationQueuingMaxQueues {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), queuing.Queues,
|
||||
fmt.Sprintf("must not be greater than %d", priorityLevelConfigurationQueuingMaxQueues)))
|
||||
}
|
||||
|
||||
if queuing.HandSize <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize, "must be positive"))
|
||||
} else if queuing.HandSize > queuing.Queues {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
|
||||
fmt.Sprintf("should not be greater than queues (%d)", queuing.Queues)))
|
||||
} else if entropy := shufflesharding.RequiredEntropyBits(int(queuing.Queues), int(queuing.HandSize)); entropy > shufflesharding.MaxHashBits {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), queuing.HandSize,
|
||||
fmt.Sprintf("required entropy bits of deckSize %d and handSize %d should not be greater than %d", queuing.Queues, queuing.HandSize, shufflesharding.MaxHashBits)))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePriorityLevelConfigurationStatus validates priority-level-configuration's status.
|
||||
func ValidatePriorityLevelConfigurationStatus(status *flowcontrol.PriorityLevelConfigurationStatus, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
keys := sets.NewString()
|
||||
for i, condition := range status.Conditions {
|
||||
if keys.Has(string(condition.Type)) {
|
||||
allErrs = append(allErrs, field.Duplicate(fldPath.Child("conditions").Index(i).Child("type"), condition.Type))
|
||||
}
|
||||
keys.Insert(string(condition.Type))
|
||||
allErrs = append(allErrs, ValidatePriorityLevelConfigurationCondition(&condition, fldPath.Child("conditions").Index(i))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePriorityLevelConfigurationCondition validates condition in priority-level-configuration's status.
|
||||
func ValidatePriorityLevelConfigurationCondition(condition *flowcontrol.PriorityLevelConfigurationCondition, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(condition.Type) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func hasWildcard(operations []string) bool {
|
||||
for _, o := range operations {
|
||||
if o == "*" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
628
pkg/apis/flowcontrol/validation/validation_test.go
Normal file
628
pkg/apis/flowcontrol/validation/validation_test.go
Normal file
@ -0,0 +1,628 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package validation
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
)
|
||||
|
||||
func TestFlowSchemaValidation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
flowSchema *flowcontrol.FlowSchema
|
||||
expectedErrors field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "missing neither resource and non-resource policy-rule should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("spec").Child("rules").Index(0), "at least one of resourceRules and nonResourceRules has to be non-empty"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normal flow-schema w/ * verbs/apiGroups/resources should work",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindGroup,
|
||||
Group: &flowcontrol.GroupSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "flow-schema mixes * verbs/apiGroups/resources should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll, "create"},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll, "tak"},
|
||||
Resources: []string{flowcontrol.ResourceAll, "tok"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"*", "create"}, "if '*' is present, must not specify other verbs"),
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("apiGroups"), []string{"*", "tak"}, "if '*' is present, must not specify other api groups"),
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("resources"), []string{"*", "tok"}, "if '*' is present, must not specify other resources"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema has both resource rules and non-resource rules should work",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
NonResourceRules: []flowcontrol.NonResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
NonResourceURLs: []string{"/apis/*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "flow-schema mixes * non-resource URLs should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
NonResourceRules: []flowcontrol.NonResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
NonResourceURLs: []string{flowcontrol.NonResourceAll, "tik"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("nonResourceRules").Index(0).Child("nonResourceURLs"), []string{"*", "tik"}, "if '*' is present, must not specify other non-resource URLs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid subject kind should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: "FooKind",
|
||||
},
|
||||
},
|
||||
NonResourceRules: []flowcontrol.NonResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
NonResourceURLs: []string{flowcontrol.NonResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind("FooKind"), supportedSubjectKinds.List()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema w/ invalid verb should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{"feed"},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"feed"}, supportedVerbs.List()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema w/ invalid priority level configuration name should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system+++$$",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindUser,
|
||||
User: &flowcontrol.UserSubject{Name: "noxu"},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("priorityLevelConfiguration").Child("name"), "system+++$$", `a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema w/ service-account kind missing namespace should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.SubjectKindServiceAccount,
|
||||
ServiceAccount: &flowcontrol.ServiceAccountSubject{
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount").Child("namespace"), "must specify namespace for service account"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema missing kind should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
MatchingPrecedence: 50,
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRulesWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: "",
|
||||
},
|
||||
},
|
||||
ResourceRules: []flowcontrol.ResourcePolicyRule{
|
||||
{
|
||||
Verbs: []string{flowcontrol.VerbAll},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind(""), supportedSubjectKinds.List()),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errs := ValidateFlowSchema(testCase.flowSchema)
|
||||
if !assert.ElementsMatch(t, testCase.expectedErrors, errs) {
|
||||
t.Logf("mismatch: %v", cmp.Diff(testCase.expectedErrors, errs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityLevelConfigurationValidation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
priorityLevelConfiguration *flowcontrol.PriorityLevelConfiguration
|
||||
expectedErrors field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "normal customized priority level should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
Queues: 512,
|
||||
HandSize: 4,
|
||||
QueueLengthLimit: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "system low priority level w/ exempt should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: flowcontrol.PriorityLevelConfigurationNameExempt,
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeExempt,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 1",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 512,
|
||||
HandSize: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("queuing").Child("handSize"), int32(8), "required entropy bits of deckSize 512 and handSize 8 should not be greater than 60"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 2",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 128,
|
||||
HandSize: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("queuing").Child("handSize"), int32(10), "required entropy bits of deckSize 128 and handSize 10 should not be greater than 60"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 3",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: math.MaxInt32,
|
||||
HandSize: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("queuing").Child("handSize"), int32(3), "required entropy bits of deckSize 2147483647 and handSize 3 should not be greater than 60"),
|
||||
field.Invalid(field.NewPath("spec").Child("queuing").Child("queues"), int32(math.MaxInt32), "must not be greater than 10000000"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ handSize=2 and queues=10^7 should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 10 * 1000 * 1000, // 10^7
|
||||
HandSize: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ handSize greater than queues should fail",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
Type: flowcontrol.PriorityLevelQueuingTypeQueueing,
|
||||
Queuing: &flowcontrol.QueuingConfiguration{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 7,
|
||||
HandSize: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("queuing").Child("handSize"), int32(8), "should not be greater than queues (7)"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errs := ValidatePriorityLevelConfiguration(testCase.priorityLevelConfiguration)
|
||||
if !assert.ElementsMatch(t, testCase.expectedErrors, errs) {
|
||||
t.Logf("mismatch: %v", cmp.Diff(testCase.expectedErrors, errs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateFlowSchemaStatus(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
status *flowcontrol.FlowSchemaStatus
|
||||
expectedErrors field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "empty status should work",
|
||||
status: &flowcontrol.FlowSchemaStatus{},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "duplicate key should fail",
|
||||
status: &flowcontrol.FlowSchemaStatus{
|
||||
Conditions: []flowcontrol.FlowSchemaCondition{
|
||||
{
|
||||
Type: "1",
|
||||
},
|
||||
{
|
||||
Type: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.FlowSchemaConditionType("1")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing key should fail",
|
||||
status: &flowcontrol.FlowSchemaStatus{
|
||||
Conditions: []flowcontrol.FlowSchemaCondition{
|
||||
{
|
||||
Type: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errs := ValidateFlowSchemaStatus(testCase.status, field.NewPath("status"))
|
||||
if !assert.ElementsMatch(t, testCase.expectedErrors, errs) {
|
||||
t.Logf("mismatch: %v", cmp.Diff(testCase.expectedErrors, errs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePriorityLevelConfigurationStatus(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
status *flowcontrol.PriorityLevelConfigurationStatus
|
||||
expectedErrors field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "empty status should work",
|
||||
status: &flowcontrol.PriorityLevelConfigurationStatus{},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "duplicate key should fail",
|
||||
status: &flowcontrol.PriorityLevelConfigurationStatus{
|
||||
Conditions: []flowcontrol.PriorityLevelConfigurationCondition{
|
||||
{
|
||||
Type: "1",
|
||||
},
|
||||
{
|
||||
Type: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.PriorityLevelConfigurationConditionType("1")),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing key should fail",
|
||||
status: &flowcontrol.PriorityLevelConfigurationStatus{
|
||||
Conditions: []flowcontrol.PriorityLevelConfigurationCondition{
|
||||
{
|
||||
Type: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
errs := ValidatePriorityLevelConfigurationStatus(testCase.status, field.NewPath("status"))
|
||||
if !assert.ElementsMatch(t, testCase.expectedErrors, errs) {
|
||||
t.Logf("mismatch: %v", cmp.Diff(testCase.expectedErrors, errs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user