mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 02:06:23 +00:00
initial REST implementation
fmt system code review: addressing review commits doc default
This commit is contained in:
committed by
Mike Spreitzer
parent
3926050733
commit
17c3da0b06
27
pkg/apis/flowcontrol/bootstrap/flowschemas.go
Normal file
27
pkg/apis/flowcontrol/bootstrap/flowschemas.go
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2016 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 bootstrap
|
||||
|
||||
import (
|
||||
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
|
||||
)
|
||||
|
||||
// SystemFlowSchemas returns a list of system preset flow schemas
|
||||
func SystemFlowSchemas() []*flowcontrolv1alpha1.FlowSchema {
|
||||
// TODO: implement system flow-schemas
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2016 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 bootstrap
|
||||
|
||||
import (
|
||||
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
|
||||
)
|
||||
|
||||
// SystemPriorityLevelConfigurations returns a list of system preset priority levels
|
||||
func SystemPriorityLevelConfigurations() []*flowcontrolv1alpha1.PriorityLevelConfiguration {
|
||||
// TODO: implement system flow-schemas
|
||||
return nil
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
flowcontrolv1alpha1 "k8s.io/kubernetes/pkg/apis/flowcontrol/v1alpha1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -32,4 +33,5 @@ func init() {
|
||||
// Install registers the API group and adds types to a scheme
|
||||
func Install(scheme *runtime.Scheme) {
|
||||
utilruntime.Must(flowcontrol.AddToScheme(scheme))
|
||||
utilruntime.Must(flowcontrolv1alpha1.AddToScheme(scheme))
|
||||
}
|
||||
|
||||
@@ -44,6 +44,22 @@ const (
|
||||
NonResourceAll = "*"
|
||||
)
|
||||
|
||||
// System preset priority level names
|
||||
const (
|
||||
PriorityLevelConfigurationNameSystemTop = "system-top"
|
||||
PriorityLevelConfigurationNameWorkloadLow = "workload-low"
|
||||
)
|
||||
|
||||
// Default settings for flow-schema
|
||||
const (
|
||||
FlowSchemaDefaultMatchingPrecedence int32 = 1000
|
||||
)
|
||||
|
||||
// Default settings for priority-level-configuration
|
||||
const (
|
||||
PriorityLevelConfigurationDefaultHandSize int32 = 1
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
36
pkg/apis/flowcontrol/v1alpha1/defaults.go
Normal file
36
pkg/apis/flowcontrol/v1alpha1/defaults.go
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/flowcontrol/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
)
|
||||
|
||||
// SetDefaults_FlowSchema sets default values for flow schema
|
||||
func SetDefaults_FlowSchema(obj *v1alpha1.FlowSchema) {
|
||||
if obj.Spec.MatchingPrecedence == 0 {
|
||||
obj.Spec.MatchingPrecedence = flowcontrol.FlowSchemaDefaultMatchingPrecedence
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefaults_FlowSchema sets default values for flow schema
|
||||
func SetDefaults_PriorityLevelConfiguration(obj *v1alpha1.PriorityLevelConfiguration) {
|
||||
if obj.Spec.HandSize == 0 {
|
||||
obj.Spec.HandSize = flowcontrol.PriorityLevelConfigurationDefaultHandSize
|
||||
}
|
||||
}
|
||||
274
pkg/apis/flowcontrol/validation/validation.go
Normal file
274
pkg/apis/flowcontrol/validation/validation.go
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
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"
|
||||
"math/big"
|
||||
|
||||
"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"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
)
|
||||
|
||||
var ValidateFlowSchemaName = apimachineryvalidation.NameIsDNSSubdomain
|
||||
|
||||
var ValidatePriorityLevelConfigurationName = apimachineryvalidation.NameIsDNSSubdomain
|
||||
|
||||
var supportedDistinguisherMethods = sets.NewString(
|
||||
string(flowcontrol.FlowDistinguisherMethodByNamespaceType),
|
||||
string(flowcontrol.FlowDistinguisherMethodByUserType),
|
||||
)
|
||||
|
||||
var supportedVerbs = sets.NewString(
|
||||
flowcontrol.VerbAll,
|
||||
"get",
|
||||
"list",
|
||||
"watch",
|
||||
"create",
|
||||
"update",
|
||||
"delete",
|
||||
"deletecollection",
|
||||
"patch",
|
||||
)
|
||||
|
||||
var supportedSubjectKinds = sets.NewString(
|
||||
flowcontrol.ServiceAccountKind,
|
||||
flowcontrol.UserKind,
|
||||
flowcontrol.GroupKind,
|
||||
)
|
||||
|
||||
const (
|
||||
maxHashBits = 60
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 to a priority level"))
|
||||
}
|
||||
if len(spec.Rules) > 0 {
|
||||
for i, rule := range spec.Rules {
|
||||
allErrs = append(allErrs, ValidateFlowSchemaPolicyRuleWithSubjects(&rule, fldPath.Child("rules").Index(i))...)
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("rules"), "rules must contain at least one value"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateFlowSchemaPolicyRuleWithSubjects(rule *flowcontrol.PolicyRuleWithSubjects, 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"))
|
||||
}
|
||||
allErrs = append(allErrs, ValidateFlowSchemaPolicyRule(&rule.Rule, fldPath.Child("rule"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateFlowSchemaSubject(subject *flowcontrol.Subject, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(subject.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
switch subject.Kind {
|
||||
case flowcontrol.ServiceAccountKind:
|
||||
if len(subject.Name) > 0 {
|
||||
for _, msg := range validation.ValidateServiceAccountName(subject.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), subject.Name, msg))
|
||||
}
|
||||
}
|
||||
if len(subject.APIGroup) > 0 {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{""}))
|
||||
}
|
||||
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"))
|
||||
}
|
||||
case flowcontrol.UserKind:
|
||||
// TODO(ericchiang): What other restrictions on user name are there?
|
||||
if subject.APIGroup != flowcontrol.GroupName {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{flowcontrol.GroupName}))
|
||||
}
|
||||
if len(subject.Namespace) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), subject.Namespace, "must not set namespace for user-kind subject"))
|
||||
}
|
||||
case flowcontrol.GroupKind:
|
||||
// TODO(ericchiang): What other restrictions on group name are there?
|
||||
if subject.APIGroup != flowcontrol.GroupName {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("apiGroup"), subject.APIGroup, []string{flowcontrol.GroupName}))
|
||||
}
|
||||
if len(subject.Namespace) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), subject.Namespace, "must not set namespace for group-kind subject"))
|
||||
}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), subject.Kind, supportedSubjectKinds.List()))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateFlowSchemaPolicyRule(rule *flowcontrol.PolicyRule, 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"))
|
||||
}
|
||||
|
||||
if len(rule.NonResourceURLs) > 0 {
|
||||
if len(rule.APIGroups) > 0 || len(rule.Resources) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "rules cannot apply to both regular resources and non-resource URLs"))
|
||||
}
|
||||
if hasWildcard(rule.NonResourceURLs) && len(rule.NonResourceURLs) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "if '*' is present, must not specify other non-resource URLs"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
if len(rule.APIGroups) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("apiGroups"), "resource rules must supply at least one api group"))
|
||||
}
|
||||
if len(rule.Resources) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("resources"), "resource rules must supply at least one resource"))
|
||||
}
|
||||
|
||||
if hasWildcard(rule.Verbs) && len(rule.Verbs) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("verbs"), rule.Verbs, "if '*' is present, must not specify other verbs"))
|
||||
}
|
||||
if hasWildcard(rule.APIGroups) && len(rule.APIGroups) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroups"), rule.APIGroups, "if '*' is present, must not specify other api groups"))
|
||||
}
|
||||
if hasWildcard(rule.Resources) && len(rule.Resources) > 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("resources"), rule.Resources, "if '*' is present, must not specify other resources"))
|
||||
}
|
||||
if !supportedVerbs.IsSuperset(sets.NewString(rule.Verbs...)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("verbs"), rule.Verbs, supportedVerbs.List()))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateFlowSchemaStatus(status *flowcontrol.FlowSchemaStatus, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
// conditions will not be validated
|
||||
return allErrs
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func ValidatePriorityLevelConfigurationSpec(spec *flowcontrol.PriorityLevelConfigurationSpec, name string, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if name != flowcontrol.PriorityLevelConfigurationNameSystemTop && spec.Exempt {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("exempt"), "must not be exempt"))
|
||||
}
|
||||
if name != flowcontrol.PriorityLevelConfigurationNameWorkloadLow && spec.GlobalDefault {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("globalDefault"), "must not be global default"))
|
||||
}
|
||||
if !spec.Exempt {
|
||||
if spec.AssuredConcurrencyShares <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("assuredConcurrencyShares"), spec.AssuredConcurrencyShares, "must be positive"))
|
||||
}
|
||||
if spec.QueueLengthLimit <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queueLengthLimit"), spec.QueueLengthLimit, "must be positive"))
|
||||
}
|
||||
if spec.Queues <= 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), spec.Queues, "must be positive"))
|
||||
}
|
||||
if spec.HandSize < 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), spec.HandSize, "must be positive"))
|
||||
}
|
||||
} else {
|
||||
if spec.AssuredConcurrencyShares != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("assuredConcurrencyShares"), spec.AssuredConcurrencyShares, "must be positive for exempt priority"))
|
||||
}
|
||||
if spec.QueueLengthLimit != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queueLengthLimit"), spec.QueueLengthLimit, "must be positive for exempt priority"))
|
||||
}
|
||||
if spec.Queues != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("queues"), spec.Queues, "must be positive for exempt priority"))
|
||||
}
|
||||
if spec.HandSize != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), spec.HandSize, "must be positive for exempt priority"))
|
||||
}
|
||||
}
|
||||
if spec.HandSize > spec.Queues {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), spec.HandSize,
|
||||
fmt.Sprintf("should not be greater than queues (%d)", spec.Queues)))
|
||||
}
|
||||
if !validateShuffleShardingParameters(spec.HandSize, spec.Queues) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("handSize"), spec.HandSize,
|
||||
fmt.Sprintf("falling factorial of handSize (%d) and queues (%d) exceeds %d bits", spec.HandSize, spec.Queues, maxHashBits)))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePriorityLevelConfigurationStatus(status *flowcontrol.PriorityLevelConfigurationStatus, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
// conditions will not be validated
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateShuffleShardingParameters(handSize, queues int32) bool {
|
||||
// TODO: performance impact from bit-int multiplication?
|
||||
v := big.NewInt(1)
|
||||
for i := int32(0); i < handSize; i++ {
|
||||
v.Mul(v, big.NewInt(int64(queues-i)))
|
||||
}
|
||||
l := v.BitLen()
|
||||
return l <= maxHashBits
|
||||
}
|
||||
|
||||
func hasWildcard(operations []string) bool {
|
||||
for _, o := range operations {
|
||||
if o == "*" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
566
pkg/apis/flowcontrol/validation/validation_test.go
Normal file
566
pkg/apis/flowcontrol/validation/validation_test.go
Normal file
@@ -0,0 +1,566 @@
|
||||
/*
|
||||
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 (
|
||||
"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"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFlowSchemaValidation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
flowSchema *flowcontrol.FlowSchema
|
||||
expectedErrors field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "empty spec should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("spec").Child("priorityLevelConfiguration").Child("name"), "must reference to a priority level"),
|
||||
field.Required(field.NewPath("spec").Child("rules"), "rules must contain at least one value"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing policy-rule should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("verbs"), "verbs must contain at least one value"),
|
||||
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("apiGroups"), "resource rules must supply at least one api group"),
|
||||
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("resources"), "resource rules must supply at least one resource"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normal flow-schema w/ * verbs/apiGroups/resources should work",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
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{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
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("rule").Child("verbs"), []string{"*", "create"}, "if '*' is present, must not specify other verbs"),
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("apiGroups"), []string{"*", "tak"}, "if '*' is present, must not specify other api groups"),
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("resources"), []string{"*", "tok"}, "if '*' is present, must not specify other resources"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema mixes non-resource URLs w/ regular resources should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
Verbs: []string{flowcontrol.VerbAll, "create"},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll, "tak"},
|
||||
Resources: []string{flowcontrol.ResourceAll, "tok"},
|
||||
NonResourceURLs: []string{flowcontrol.NonResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("nonResourceURLs"), []string{"*"}, "rules cannot apply to both regular resources and non-resource URLs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema mixes * non-resource URLs should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
Verbs: []string{"*"},
|
||||
NonResourceURLs: []string{flowcontrol.NonResourceAll, "tik"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("nonResourceURLs"), []string{"*", "tik"}, "if '*' is present, must not specify other non-resource URLs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normal flow-schema mixes user should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
Verbs: []string{"*"},
|
||||
NonResourceURLs: []string{flowcontrol.NonResourceAll, "tik"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("rule").Child("nonResourceURLs"), []string{"*", "tik"}, "if '*' is present, must not specify other non-resource URLs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "flow-schema w/ invalid verb should fail",
|
||||
flowSchema: &flowcontrol.FlowSchema{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.FlowSchemaSpec{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
Verbs: []string{"feed"},
|
||||
APIGroups: []string{flowcontrol.APIGroupAll},
|
||||
Resources: []string{flowcontrol.ResourceAll},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("rule").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{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system+++$$",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.UserKind,
|
||||
APIGroup: flowcontrol.GroupName,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
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{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: flowcontrol.ServiceAccountKind,
|
||||
Name: "noxu",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
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("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{
|
||||
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
|
||||
Name: "system-bar",
|
||||
},
|
||||
Rules: []flowcontrol.PolicyRuleWithSubjects{
|
||||
{
|
||||
Subjects: []flowcontrol.Subject{
|
||||
{
|
||||
Kind: "",
|
||||
},
|
||||
},
|
||||
Rule: flowcontrol.PolicyRule{
|
||||
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("name"), ""),
|
||||
field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), "", 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{
|
||||
GlobalDefault: false,
|
||||
Exempt: false,
|
||||
AssuredConcurrencyShares: 100,
|
||||
Queues: 512,
|
||||
HandSize: 4,
|
||||
QueueLengthLimit: 100,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ global-default should fail",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
GlobalDefault: true,
|
||||
Exempt: false,
|
||||
AssuredConcurrencyShares: 100,
|
||||
Queues: 512,
|
||||
HandSize: 4,
|
||||
QueueLengthLimit: 100,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Forbidden(field.NewPath("spec").Child("globalDefault"), "must not be global default"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "system top priority level w/ global-default should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: flowcontrol.PriorityLevelConfigurationNameWorkloadLow,
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
GlobalDefault: true,
|
||||
Exempt: false,
|
||||
AssuredConcurrencyShares: 100,
|
||||
Queues: 512,
|
||||
HandSize: 4,
|
||||
QueueLengthLimit: 100,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ exempt should fail",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
GlobalDefault: false,
|
||||
Exempt: true,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Forbidden(field.NewPath("spec").Child("exempt"), "must not be exempt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "system low priority level w/ exempt should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: flowcontrol.PriorityLevelConfigurationNameSystemTop,
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
GlobalDefault: false,
|
||||
Exempt: true,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ empty spec should fail",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("assuredConcurrencyShares"), int32(0), "must be positive"),
|
||||
field.Invalid(field.NewPath("spec").Child("queueLengthLimit"), int32(0), "must be positive"),
|
||||
field.Invalid(field.NewPath("spec").Child("queues"), int32(0), "must be positive"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 1",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 512,
|
||||
HandSize: 8,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("handSize"), int32(8), "falling factorial of handSize (8) and queues (512) exceeds 60 bits"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 2",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 128,
|
||||
HandSize: 10,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("handSize"), int32(10), "falling factorial of handSize (10) and queues (128) exceeds 60 bits"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ overflowing handSize/queues should fail 3",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: math.MaxInt32,
|
||||
HandSize: 3,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").Child("handSize"), int32(3), "falling factorial of handSize (3) and queues (2147483647) exceeds 60 bits"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "customized priority level w/ handSize=2 and queues=max-int32 should work",
|
||||
priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "system-foo",
|
||||
},
|
||||
Spec: flowcontrol.PriorityLevelConfigurationSpec{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 1<<30 - 1, // max integer in 30 bits
|
||||
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{
|
||||
AssuredConcurrencyShares: 100,
|
||||
QueueLengthLimit: 100,
|
||||
Queues: 7,
|
||||
HandSize: 8,
|
||||
},
|
||||
},
|
||||
expectedErrors: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec").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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
18
pkg/registry/flowcontrol/flowschema/doc.go
Normal file
18
pkg/registry/flowcontrol/flowschema/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 flowschema provides model implementation of flow-schema api
|
||||
package flowschema // import "k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
|
||||
113
pkg/registry/flowcontrol/flowschema/storage/storage.go
Normal file
113
pkg/registry/flowcontrol/flowschema/storage/storage.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
flowcontrolbootstrap "k8s.io/kubernetes/pkg/apis/flowcontrol/bootstrap"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
|
||||
)
|
||||
|
||||
// Container includes dummy storage for RC pods and experimental storage for Scale.
|
||||
type FlowSchemaStorage struct {
|
||||
FlowSchema *REST
|
||||
Status *StatusREST
|
||||
}
|
||||
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter) FlowSchemaStorage {
|
||||
flowSchemaREST, flowSchemaStatusREST := NewREST(optsGetter)
|
||||
|
||||
return FlowSchemaStorage{
|
||||
FlowSchema: flowSchemaREST,
|
||||
Status: flowSchemaStatusREST,
|
||||
}
|
||||
}
|
||||
|
||||
// REST implements a RESTStorage for jobs against etcd
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against Jobs.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &flowcontrol.FlowSchema{} },
|
||||
NewListFunc: func() runtime.Object { return &flowcontrol.FlowSchema{} },
|
||||
DefaultQualifiedResource: flowcontrol.Resource("flowschemas"),
|
||||
|
||||
CreateStrategy: flowschema.Strategy,
|
||||
UpdateStrategy: flowschema.Strategy,
|
||||
DeleteStrategy: flowschema.Strategy,
|
||||
|
||||
// TODO: develop printer handler for flowschema resource
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = flowschema.StatusStrategy
|
||||
|
||||
return &REST{store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// Delete ensures that system priority classes are not deleted.
|
||||
func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
for _, pl := range flowcontrolbootstrap.SystemPriorityLevelConfigurations() {
|
||||
if name == pl.Name {
|
||||
return nil, false, apierrors.NewForbidden(flowcontrol.Resource("flowschemas"), pl.Name, errors.New("this is a system flow schema and cannot be deleted"))
|
||||
}
|
||||
}
|
||||
|
||||
return r.Store.Delete(ctx, name, deleteValidation, options)
|
||||
}
|
||||
|
||||
// StatusREST implements the REST endpoint for changing the status of a resourcequota.
|
||||
type StatusREST struct {
|
||||
store *genericregistry.Store
|
||||
}
|
||||
|
||||
// New creates a new Job object.
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
return &flowcontrol.FlowSchema{}
|
||||
}
|
||||
|
||||
// Get retrieves the object from the storage. It is required to support Patch.
|
||||
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return r.store.Get(ctx, name, options)
|
||||
}
|
||||
|
||||
// Update alters the status subset of an object.
|
||||
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
|
||||
// subresources should never allow create on update.
|
||||
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
|
||||
}
|
||||
101
pkg/registry/flowcontrol/flowschema/strategy.go
Normal file
101
pkg/registry/flowcontrol/flowschema/strategy.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 flowschema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
||||
)
|
||||
|
||||
// flowSchemaStrategy implements verification logic for FlowSchema.
|
||||
type flowSchemaStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating Replication Controller objects.
|
||||
var Strategy = flowSchemaStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped returns false because all PriorityClasses are global.
|
||||
func (flowSchemaStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrepareForCreate clears the status of a flow-schema before creation.
|
||||
func (flowSchemaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
fl := obj.(*flowcontrol.FlowSchema)
|
||||
fl.Status = flowcontrol.FlowSchemaStatus{}
|
||||
fl.Generation = 1
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (flowSchemaStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newFlowSchema := obj.(*flowcontrol.FlowSchema)
|
||||
oldFlowSchema := old.(*flowcontrol.FlowSchema)
|
||||
|
||||
// Spec updates bump the generation so that we can distinguish between status updates.
|
||||
if !apiequality.Semantic.DeepEqual(newFlowSchema.Spec, oldFlowSchema.Spec) {
|
||||
newFlowSchema.Generation = oldFlowSchema.Generation + 1
|
||||
}
|
||||
newFlowSchema.Status = oldFlowSchema.Status
|
||||
}
|
||||
|
||||
// Validate validates a new flow-schema.
|
||||
func (flowSchemaStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
return validation.ValidateFlowSchema(obj.(*flowcontrol.FlowSchema))
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (flowSchemaStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (flowSchemaStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is false for flow-schemas; this means a POST is needed to create one.
|
||||
func (flowSchemaStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (flowSchemaStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
return validation.ValidateFlowSchema(obj.(*flowcontrol.FlowSchema))
|
||||
}
|
||||
|
||||
type flowSchemaStatusStrategy struct {
|
||||
flowSchemaStrategy
|
||||
}
|
||||
|
||||
var StatusStrategy = flowSchemaStatusStrategy{Strategy}
|
||||
|
||||
func (flowSchemaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newFlowSchema := obj.(*flowcontrol.FlowSchema)
|
||||
oldFlowSchema := old.(*flowcontrol.FlowSchema)
|
||||
newFlowSchema.Spec = oldFlowSchema.Spec
|
||||
}
|
||||
|
||||
func (flowSchemaStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
return validation.ValidateFlowSchema(obj.(*flowcontrol.FlowSchema))
|
||||
}
|
||||
18
pkg/registry/flowcontrol/prioritylevelconfiguration/doc.go
Normal file
18
pkg/registry/flowcontrol/prioritylevelconfiguration/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 prioritylevelconfiguration provides model implementation of priority-level-configuration api
|
||||
package prioritylevelconfiguration // import "k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration"
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
flowcontrolbootstrap "k8s.io/kubernetes/pkg/apis/flowcontrol/bootstrap"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
|
||||
"k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration"
|
||||
)
|
||||
|
||||
// Container includes dummy storage for RC pods and experimental storage for Scale.
|
||||
type PriorityLevelConfigurationStorage struct {
|
||||
PriorityLevelConfiguration *REST
|
||||
Status *StatusREST
|
||||
}
|
||||
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter) PriorityLevelConfigurationStorage {
|
||||
priorityLevelConfigurationREST, priorityLevelConfigurationStatusREST := NewREST(optsGetter)
|
||||
|
||||
return PriorityLevelConfigurationStorage{
|
||||
PriorityLevelConfiguration: priorityLevelConfigurationREST,
|
||||
Status: priorityLevelConfigurationStatusREST,
|
||||
}
|
||||
}
|
||||
|
||||
// REST implements a RESTStorage for jobs against etcd
|
||||
type REST struct {
|
||||
*genericregistry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against Jobs.
|
||||
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) {
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: func() runtime.Object { return &flowcontrol.PriorityLevelConfiguration{} },
|
||||
NewListFunc: func() runtime.Object { return &flowcontrol.PriorityLevelConfiguration{} },
|
||||
DefaultQualifiedResource: flowcontrol.Resource("prioritylevelconfigurations"),
|
||||
|
||||
CreateStrategy: prioritylevelconfiguration.Strategy,
|
||||
UpdateStrategy: prioritylevelconfiguration.Strategy,
|
||||
DeleteStrategy: prioritylevelconfiguration.Strategy,
|
||||
|
||||
// TODO: develop printer handler for prioritylevelconfiguration resource
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
panic(err) // TODO: Propagate error up
|
||||
}
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = flowschema.StatusStrategy
|
||||
|
||||
return &REST{store}, &StatusREST{store: &statusStore}
|
||||
}
|
||||
|
||||
// Delete ensures that system priority classes are not deleted.
|
||||
func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
for _, pl := range flowcontrolbootstrap.SystemPriorityLevelConfigurations() {
|
||||
if name == pl.Name {
|
||||
return nil, false, apierrors.NewForbidden(flowcontrol.Resource("prioritylevelconfigurations"), pl.Name, errors.New("this is a system priority level and cannot be deleted"))
|
||||
}
|
||||
}
|
||||
|
||||
return r.Store.Delete(ctx, name, deleteValidation, options)
|
||||
}
|
||||
|
||||
// StatusREST implements the REST endpoint for changing the status of a resourcequota.
|
||||
type StatusREST struct {
|
||||
store *genericregistry.Store
|
||||
}
|
||||
|
||||
// New creates a new Job object.
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
return &flowcontrol.FlowSchema{}
|
||||
}
|
||||
|
||||
// Get retrieves the object from the storage. It is required to support Patch.
|
||||
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return r.store.Get(ctx, name, options)
|
||||
}
|
||||
|
||||
// Update alters the status subset of an object.
|
||||
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
|
||||
// subresources should never allow create on update.
|
||||
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
|
||||
}
|
||||
101
pkg/registry/flowcontrol/prioritylevelconfiguration/strategy.go
Normal file
101
pkg/registry/flowcontrol/prioritylevelconfiguration/strategy.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 prioritylevelconfiguration
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
||||
)
|
||||
|
||||
// flowSchemaStrategy implements verification logic for FlowSchema.
|
||||
type priorityLevelConfigurationStrategy struct {
|
||||
runtime.ObjectTyper
|
||||
names.NameGenerator
|
||||
}
|
||||
|
||||
// Strategy is the default logic that applies when creating and updating Replication Controller objects.
|
||||
var Strategy = priorityLevelConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||
|
||||
// NamespaceScoped returns false because all PriorityClasses are global.
|
||||
func (priorityLevelConfigurationStrategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// PrepareForCreate clears the status of a priority-level-configuration before creation.
|
||||
func (priorityLevelConfigurationStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
pl := obj.(*flowcontrol.PriorityLevelConfiguration)
|
||||
pl.Status = flowcontrol.PriorityLevelConfigurationStatus{}
|
||||
pl.Generation = 1
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (priorityLevelConfigurationStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration)
|
||||
oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration)
|
||||
|
||||
// Spec updates bump the generation so that we can distinguish between status updates.
|
||||
if !apiequality.Semantic.DeepEqual(newPriorityLevelConfiguration.Spec, oldPriorityLevelConfiguration.Spec) {
|
||||
newPriorityLevelConfiguration.Generation = oldPriorityLevelConfiguration.Generation + 1
|
||||
}
|
||||
newPriorityLevelConfiguration.Status = oldPriorityLevelConfiguration.Status
|
||||
}
|
||||
|
||||
// Validate validates a new flow-schema.
|
||||
func (priorityLevelConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration))
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (priorityLevelConfigurationStrategy) Canonicalize(obj runtime.Object) {
|
||||
}
|
||||
|
||||
func (priorityLevelConfigurationStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is false for priority-level-configurations; this means a POST is needed to create one.
|
||||
func (priorityLevelConfigurationStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration))
|
||||
}
|
||||
|
||||
type priorityLevelConfigurationStatusStrategy struct {
|
||||
priorityLevelConfigurationStrategy
|
||||
}
|
||||
|
||||
var StatusStrategy = priorityLevelConfigurationStatusStrategy{Strategy}
|
||||
|
||||
func (priorityLevelConfigurationStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration)
|
||||
oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration)
|
||||
newPriorityLevelConfiguration.Spec = oldPriorityLevelConfiguration.Spec
|
||||
}
|
||||
|
||||
func (priorityLevelConfigurationStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration))
|
||||
}
|
||||
148
pkg/registry/flowcontrol/rest/storage_flowcontrol.go
Normal file
148
pkg/registry/flowcontrol/rest/storage_flowcontrol.go
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
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 rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
flowcontrolclient "k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/bootstrap"
|
||||
flowschemastore "k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema/storage"
|
||||
prioritylevelconfigurationstore "k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration/storage"
|
||||
)
|
||||
|
||||
const PostStartHookName = "apiserver/bootstrap-system-flowcontrol-configuration"
|
||||
|
||||
type RESTStorageProvider struct{}
|
||||
|
||||
var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
|
||||
|
||||
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(flowcontrol.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
|
||||
|
||||
if apiResourceConfigSource.VersionEnabled(flowcontrolv1alpha1.SchemeGroupVersion) {
|
||||
apiGroupInfo.VersionedResourcesStorageMap[flowcontrolv1alpha1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
|
||||
}
|
||||
return apiGroupInfo, true
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
// flow-schema
|
||||
flowSchemaStorage, flowSchemaStatusStorage := flowschemastore.NewREST(restOptionsGetter)
|
||||
storage["flowschemas"] = flowSchemaStorage
|
||||
storage["flowschemas/status"] = flowSchemaStatusStorage
|
||||
|
||||
// priority-level-configuration
|
||||
priorityLevelConfigurationStorage, priorityLevelConfigurationStatusStorage := prioritylevelconfigurationstore.NewREST(restOptionsGetter)
|
||||
storage["prioritylevelconfigurations"] = priorityLevelConfigurationStorage
|
||||
storage["prioritylevelconfigurations/status"] = priorityLevelConfigurationStatusStorage
|
||||
|
||||
return storage
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
|
||||
systemPreset := SystemPresetData{
|
||||
FlowSchemas: bootstrap.SystemFlowSchemas(),
|
||||
PriorityLevelConfigurations: bootstrap.SystemPriorityLevelConfigurations(),
|
||||
}
|
||||
// TODO: default flow-schemas and priority levels
|
||||
return PostStartHookName, systemPreset.EnsureSystemPresetConfiguration(), nil
|
||||
}
|
||||
|
||||
type SystemPresetData struct {
|
||||
FlowSchemas []*flowcontrolv1alpha1.FlowSchema
|
||||
PriorityLevelConfigurations []*flowcontrolv1alpha1.PriorityLevelConfiguration
|
||||
}
|
||||
|
||||
func (d SystemPresetData) EnsureSystemPresetConfiguration() genericapiserver.PostStartHookFunc {
|
||||
return func(hookContext genericapiserver.PostStartHookContext) error {
|
||||
flowcontrolClientSet := flowcontrolclient.NewForConfigOrDie(hookContext.LoopbackClientConfig)
|
||||
// Adding system priority classes is important. If they fail to add, many critical system
|
||||
// components may fail and cluster may break.
|
||||
err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, flowSchema := range d.FlowSchemas {
|
||||
_, err := flowcontrolClientSet.FlowSchemas().Get(flowSchema.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err := flowcontrolClientSet.FlowSchemas().Create(flowSchema)
|
||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return false, err
|
||||
} else if err == nil {
|
||||
klog.V(6).Infof("created system preset FlowSchema %s", flowSchema.Name)
|
||||
} else {
|
||||
klog.V(6).Infof("system preset FlowSchema %s already exists, skipping creating", flowSchema.Name)
|
||||
}
|
||||
} else {
|
||||
// Unable to get the priority class for reasons other than "not found".
|
||||
klog.Warningf("unable to get FlowSchema %v: %v. Retrying...", flowSchema.Name, err)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, priorityLevelConfiguration := range d.PriorityLevelConfigurations {
|
||||
_, err := flowcontrolClientSet.PriorityLevelConfigurations().Get(priorityLevelConfiguration.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err := flowcontrolClientSet.PriorityLevelConfigurations().Create(priorityLevelConfiguration)
|
||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return false, err
|
||||
} else if err == nil {
|
||||
klog.V(6).Infof("created system preset PriorityLevelConfiguration %s", priorityLevelConfiguration.Name)
|
||||
} else {
|
||||
klog.V(6).Infof("system preset PriorityLevelConfiguration %s already exists, skipping creating", priorityLevelConfiguration.Name)
|
||||
}
|
||||
} else if err == nil {
|
||||
// Unable to get the priority class for reasons other than "not found".
|
||||
klog.Warningf("unable to get PriorityLevelConfiguration %v: %v. Retrying...", priorityLevelConfiguration.Name, err)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
klog.V(4).Infof("all system flow-control settings are created successfully or already exist.")
|
||||
return true, nil
|
||||
})
|
||||
// if we're never able to make it through initialization, kill the API server.
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to add default system flow-control settings: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p RESTStorageProvider) GroupName() string {
|
||||
return flowcontrol.GroupName
|
||||
}
|
||||
Reference in New Issue
Block a user