Skip to content

Commit 71050b6

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#60519 from bsalamat/auto_prio_class
Automatic merge from submit-queue (batch tested with PRs 60519, 61099, 61218, 61166, 61714). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Automatically add system critical priority classes at cluster boostrapping **What this PR does / why we need it**: We had two PriorityClasses that were hardcoded and special cased in our code base. These two priority classes never existed in API server. Priority admission controller had code to resolve these two names. This PR removes the hardcoded PriorityClasses and adds code to create these PriorityClasses automatically when API server starts. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes kubernetes#60178 ref/ kubernetes#57471 **Special notes for your reviewer**: **Release note**: ```release-note Automatically add system critical priority classes at cluster boostrapping. ``` /sig scheduling
2 parents 2bd8a7d + 9592a9e commit 71050b6

File tree

21 files changed

+347
-97
lines changed

21 files changed

+347
-97
lines changed

hack/.golint_failures

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,6 @@ pkg/registry/storage/rest
323323
pkg/registry/storage/storageclass
324324
pkg/registry/storage/storageclass/storage
325325
pkg/routes
326-
pkg/scheduler/api
327326
pkg/security/apparmor
328327
pkg/security/podsecuritypolicy
329328
pkg/security/podsecuritypolicy/group

pkg/apis/scheduling/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package(default_visibility = ["//visibility:public"])
33
load(
44
"@io_bazel_rules_go//go:def.bzl",
55
"go_library",
6+
"go_test",
67
)
78

89
go_library(
910
name = "go_default_library",
1011
srcs = [
1112
"doc.go",
13+
"helpers.go",
1214
"register.go",
1315
"types.go",
1416
"zz_generated.deepcopy.go",
@@ -39,3 +41,10 @@ filegroup(
3941
],
4042
tags = ["automanaged"],
4143
)
44+
45+
go_test(
46+
name = "go_default_test",
47+
srcs = ["helpers_test.go"],
48+
embed = [":go_default_library"],
49+
deps = ["//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library"],
50+
)

pkg/apis/scheduling/helpers.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package scheduling
18+
19+
import (
20+
"fmt"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
)
23+
24+
// SystemPriorityClasses define system priority classes that are auto-created at cluster bootstrapping.
25+
// Our API validation logic ensures that any priority class that has a system prefix or its value
26+
// is higher than HighestUserDefinablePriority is equal to one of these SystemPriorityClasses.
27+
var systemPriorityClasses = []*PriorityClass{
28+
{
29+
ObjectMeta: metav1.ObjectMeta{
30+
Name: SystemNodeCritical,
31+
},
32+
Value: SystemCriticalPriority + 1000,
33+
Description: "Used for system critical pods that must not be moved from their current node.",
34+
},
35+
{
36+
ObjectMeta: metav1.ObjectMeta{
37+
Name: SystemClusterCritical,
38+
},
39+
Value: SystemCriticalPriority,
40+
Description: "Used for system critical pods that must run in the cluster, but can be moved to another node if necessary.",
41+
},
42+
}
43+
44+
// SystemPriorityClasses returns the list of system priority classes.
45+
// NOTE: be careful not to modify any of elements of the returned array directly.
46+
func SystemPriorityClasses() []*PriorityClass {
47+
return systemPriorityClasses
48+
}
49+
50+
// IsKnownSystemPriorityClass checks that "pc" is equal to one of the system PriorityClasses.
51+
// It ignores "description", labels, annotations, etc. of the PriorityClass.
52+
func IsKnownSystemPriorityClass(pc *PriorityClass) (bool, error) {
53+
for _, spc := range systemPriorityClasses {
54+
if spc.Name == pc.Name {
55+
if spc.Value != pc.Value {
56+
return false, fmt.Errorf("value of %v PriorityClass must be %v", spc.Name, spc.Value)
57+
}
58+
if spc.GlobalDefault != pc.GlobalDefault {
59+
return false, fmt.Errorf("globalDefault of %v PriorityClass must be %v", spc.Name, spc.GlobalDefault)
60+
}
61+
return true, nil
62+
}
63+
}
64+
return false, fmt.Errorf("%v is not a known system priority class", pc.Name)
65+
}

pkg/apis/scheduling/helpers_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package scheduling
18+
19+
import (
20+
"testing"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
)
24+
25+
func TestIsKnownSystemPriorityClass(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
pc *PriorityClass
29+
expected bool
30+
}{
31+
{
32+
name: "system priority class",
33+
pc: SystemPriorityClasses()[0],
34+
expected: true,
35+
},
36+
{
37+
name: "non-system priority class",
38+
pc: &PriorityClass{
39+
ObjectMeta: metav1.ObjectMeta{
40+
Name: SystemNodeCritical,
41+
},
42+
Value: SystemCriticalPriority, // This is the value of system cluster critical
43+
Description: "Used for system critical pods that must not be moved from their current node.",
44+
},
45+
expected: false,
46+
},
47+
}
48+
49+
for _, test := range tests {
50+
if is, err := IsKnownSystemPriorityClass(test.pc); test.expected != is {
51+
t.Errorf("Test [%v]: Expected %v, but got %v. Error: %v", test.name, test.expected, is, err)
52+
}
53+
}
54+
}

pkg/apis/scheduling/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ const (
2323
// that do not specify any priority class and there is no priority class
2424
// marked as default.
2525
DefaultPriorityWhenNoDefaultClassExists = 0
26+
// HighestUserDefinablePriority is the highest priority for user defined priority classes. Priority values larger than 1 billion are reserved for Kubernetes system use.
27+
HighestUserDefinablePriority = int32(1000000000)
28+
// SystemCriticalPriority is the beginning of the range of priority values for critical system components.
29+
SystemCriticalPriority = 2 * HighestUserDefinablePriority
2630
// SystemPriorityClassPrefix is the prefix reserved for system priority class names. Other priority
2731
// classes are not allowed to start with this prefix.
2832
SystemPriorityClassPrefix = "system-"
33+
// NOTE: In order to avoid conflict of names with user-defined priority classes, all the names must
34+
// start with SystemPriorityClassPrefix.
35+
SystemClusterCritical = SystemPriorityClassPrefix + "cluster-critical"
36+
SystemNodeCritical = SystemPriorityClassPrefix + "node-critical"
2937
)
3038

3139
// +genclient

pkg/apis/scheduling/validation/validation.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,29 @@ limitations under the License.
1717
package validation
1818

1919
import (
20+
"fmt"
2021
"strings"
2122

2223
"k8s.io/apimachinery/pkg/util/validation/field"
2324
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
2425
"k8s.io/kubernetes/pkg/apis/scheduling"
2526
)
2627

27-
// ValidatePriorityClassName checks whether the given priority class name is valid.
28-
func ValidatePriorityClassName(name string, prefix bool) []string {
29-
var allErrs []string
30-
if strings.HasPrefix(name, scheduling.SystemPriorityClassPrefix) {
31-
allErrs = append(allErrs, "priority class names with '"+scheduling.SystemPriorityClassPrefix+"' prefix are reserved for system use only")
32-
}
33-
allErrs = append(allErrs, apivalidation.NameIsDNSSubdomain(name, prefix)...)
34-
return allErrs
35-
}
36-
3728
// ValidatePriorityClass tests whether required fields in the PriorityClass are
3829
// set correctly.
3930
func ValidatePriorityClass(pc *scheduling.PriorityClass) field.ErrorList {
4031
allErrs := field.ErrorList{}
41-
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&pc.ObjectMeta, false, ValidatePriorityClassName, field.NewPath("metadata"))...)
42-
// The "Value" field can be any valid integer. So, no need to validate.
32+
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&pc.ObjectMeta, false, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...)
33+
// If the priorityClass starts with a system prefix, it must be one of the
34+
// predefined system priority classes.
35+
if strings.HasPrefix(pc.Name, scheduling.SystemPriorityClassPrefix) {
36+
if is, err := scheduling.IsKnownSystemPriorityClass(pc); !is {
37+
allErrs = append(allErrs, field.Forbidden(field.NewPath("metadata", "name"), "priority class names with '"+scheduling.SystemPriorityClassPrefix+"' prefix are reserved for system use only. error: "+err.Error()))
38+
}
39+
} else if pc.Value > scheduling.HighestUserDefinablePriority {
40+
// Non-system critical priority classes are not allowed to have a value larger than HighestUserDefinablePriority.
41+
allErrs = append(allErrs, field.Forbidden(field.NewPath("value"), fmt.Sprintf("maximum allowed value of a user defined priority is %v", scheduling.HighestUserDefinablePriority)))
42+
}
4343
return allErrs
4444
}
4545

pkg/apis/scheduling/validation/validation_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
)
2626

2727
func TestValidatePriorityClass(t *testing.T) {
28+
spcs := scheduling.SystemPriorityClasses()
2829
successCases := map[string]scheduling.PriorityClass{
2930
"no description": {
3031
ObjectMeta: metav1.ObjectMeta{Name: "tier1", Namespace: ""},
@@ -36,6 +37,12 @@ func TestValidatePriorityClass(t *testing.T) {
3637
GlobalDefault: false,
3738
Description: "Used for the highest priority pods.",
3839
},
40+
"system node critical": {
41+
ObjectMeta: metav1.ObjectMeta{Name: spcs[0].Name, Namespace: ""},
42+
Value: spcs[0].Value,
43+
GlobalDefault: spcs[0].GlobalDefault,
44+
Description: "system priority class 0",
45+
},
3946
}
4047

4148
for k, v := range successCases {
@@ -53,9 +60,15 @@ func TestValidatePriorityClass(t *testing.T) {
5360
ObjectMeta: metav1.ObjectMeta{Name: "tier&1", Namespace: ""},
5461
Value: 100,
5562
},
56-
"invalid system name": {
57-
ObjectMeta: metav1.ObjectMeta{Name: scheduling.SystemPriorityClassPrefix + "test"},
58-
Value: 100,
63+
"incorrect system class name": {
64+
ObjectMeta: metav1.ObjectMeta{Name: spcs[0].Name, Namespace: ""},
65+
Value: 0,
66+
GlobalDefault: spcs[0].GlobalDefault,
67+
},
68+
"incorrect system class value": {
69+
ObjectMeta: metav1.ObjectMeta{Name: "system-something", Namespace: ""},
70+
Value: spcs[0].Value,
71+
GlobalDefault: spcs[0].GlobalDefault,
5972
},
6073
}
6174

pkg/kubelet/types/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ go_library(
1818
importpath = "k8s.io/kubernetes/pkg/kubelet/types",
1919
deps = [
2020
"//pkg/apis/core:go_default_library",
21-
"//pkg/scheduler/api:go_default_library",
21+
"//pkg/apis/scheduling:go_default_library",
2222
"//vendor/k8s.io/api/core/v1:go_default_library",
2323
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2424
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",

pkg/kubelet/types/pod_update.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"k8s.io/api/core/v1"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2424
kubeapi "k8s.io/kubernetes/pkg/apis/core"
25-
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
25+
"k8s.io/kubernetes/pkg/apis/scheduling"
2626
)
2727

2828
const (
@@ -168,7 +168,7 @@ func IsCriticalPodBasedOnPriority(ns string, priority int32) bool {
168168
if ns != kubeapi.NamespaceSystem {
169169
return false
170170
}
171-
if priority >= schedulerapi.SystemCriticalPriority {
171+
if priority >= scheduling.SystemCriticalPriority {
172172
return true
173173
}
174174
return false

pkg/registry/scheduling/priorityclass/storage/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ go_test(
1717
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
1818
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
1919
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
20+
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
2021
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
2122
"//vendor/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
2223
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
@@ -30,7 +31,10 @@ go_library(
3031
deps = [
3132
"//pkg/apis/scheduling:go_default_library",
3233
"//pkg/registry/scheduling/priorityclass:go_default_library",
34+
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
35+
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
3336
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
37+
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
3438
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
3539
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
3640
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",

pkg/registry/scheduling/priorityclass/storage/storage.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ limitations under the License.
1717
package storage
1818

1919
import (
20+
"errors"
21+
22+
apierrors "k8s.io/apimachinery/pkg/api/errors"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2024
"k8s.io/apimachinery/pkg/runtime"
25+
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
2126
"k8s.io/apiserver/pkg/registry/generic"
2227
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
2328
"k8s.io/apiserver/pkg/registry/rest"
24-
schedulingapi "k8s.io/kubernetes/pkg/apis/scheduling"
29+
"k8s.io/kubernetes/pkg/apis/scheduling"
2530
"k8s.io/kubernetes/pkg/registry/scheduling/priorityclass"
2631
)
2732

@@ -33,9 +38,9 @@ type REST struct {
3338
// NewREST returns a RESTStorage object that will work against priority classes.
3439
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
3540
store := &genericregistry.Store{
36-
NewFunc: func() runtime.Object { return &schedulingapi.PriorityClass{} },
37-
NewListFunc: func() runtime.Object { return &schedulingapi.PriorityClassList{} },
38-
DefaultQualifiedResource: schedulingapi.Resource("priorityclasses"),
41+
NewFunc: func() runtime.Object { return &scheduling.PriorityClass{} },
42+
NewListFunc: func() runtime.Object { return &scheduling.PriorityClassList{} },
43+
DefaultQualifiedResource: scheduling.Resource("priorityclasses"),
3944

4045
CreateStrategy: priorityclass.Strategy,
4146
UpdateStrategy: priorityclass.Strategy,
@@ -56,3 +61,14 @@ var _ rest.ShortNamesProvider = &REST{}
5661
func (r *REST) ShortNames() []string {
5762
return []string{"pc"}
5863
}
64+
65+
// Delete ensures that system priority classes are not deleted.
66+
func (r *REST) Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
67+
for _, spc := range scheduling.SystemPriorityClasses() {
68+
if name == spc.Name {
69+
return nil, false, apierrors.NewForbidden(scheduling.Resource("priorityclasses"), spc.Name, errors.New("this is a system priority class and cannot be deleted"))
70+
}
71+
}
72+
73+
return r.Store.Delete(ctx, name, options)
74+
}

pkg/registry/scheduling/priorityclass/storage/storage_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"k8s.io/apimachinery/pkg/fields"
2424
"k8s.io/apimachinery/pkg/labels"
2525
"k8s.io/apimachinery/pkg/runtime"
26+
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
2627
"k8s.io/apiserver/pkg/registry/generic"
2728
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
2829
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
@@ -105,6 +106,22 @@ func TestDelete(t *testing.T) {
105106
test.TestDelete(validNewPriorityClass())
106107
}
107108

109+
// TestDeleteSystemPriorityClass checks that system priority classes cannot be deleted.
110+
func TestDeleteSystemPriorityClass(t *testing.T) {
111+
storage, server := newStorage(t)
112+
defer server.Terminate(t)
113+
defer storage.Store.DestroyFunc()
114+
key := "test/system-node-critical"
115+
ctx := genericapirequest.NewContext()
116+
pc := scheduling.SystemPriorityClasses()[0]
117+
if err := storage.Store.Storage.Create(ctx, key, pc, nil, 0); err != nil {
118+
t.Fatalf("unexpected error: %v", err)
119+
}
120+
if _, _, err := storage.Delete(ctx, pc.Name, nil); err == nil {
121+
t.Error("expected to receive an error")
122+
}
123+
}
124+
108125
func TestGet(t *testing.T) {
109126
storage, server := newStorage(t)
110127
defer server.Terminate(t)

pkg/registry/scheduling/priorityclass/strategy.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ func (priorityClassStrategy) AllowCreateOnUpdate() bool {
6868

6969
// ValidateUpdate is the default update validation for an end user.
7070
func (priorityClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
71-
validationErrorList := validation.ValidatePriorityClass(obj.(*scheduling.PriorityClass))
72-
updateErrorList := validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass))
73-
return append(validationErrorList, updateErrorList...)
71+
return validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass))
7472
}
7573

7674
// AllowUnconditionalUpdate is the default update policy for PriorityClass objects.

0 commit comments

Comments
 (0)