Allow system critical priority classes in API validation

This commit is contained in:
Bobby (Babak) Salamat
2018-03-01 00:47:48 -08:00
parent 515ba9e8d4
commit 9592a9ecf4
12 changed files with 193 additions and 78 deletions

View File

@@ -17,11 +17,16 @@ limitations under the License.
package storage
import (
"errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
schedulingapi "k8s.io/kubernetes/pkg/apis/scheduling"
"k8s.io/kubernetes/pkg/apis/scheduling"
"k8s.io/kubernetes/pkg/registry/scheduling/priorityclass"
)
@@ -33,9 +38,9 @@ type REST struct {
// NewREST returns a RESTStorage object that will work against priority classes.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &schedulingapi.PriorityClass{} },
NewListFunc: func() runtime.Object { return &schedulingapi.PriorityClassList{} },
DefaultQualifiedResource: schedulingapi.Resource("priorityclasses"),
NewFunc: func() runtime.Object { return &scheduling.PriorityClass{} },
NewListFunc: func() runtime.Object { return &scheduling.PriorityClassList{} },
DefaultQualifiedResource: scheduling.Resource("priorityclasses"),
CreateStrategy: priorityclass.Strategy,
UpdateStrategy: priorityclass.Strategy,
@@ -56,3 +61,14 @@ var _ rest.ShortNamesProvider = &REST{}
func (r *REST) ShortNames() []string {
return []string{"pc"}
}
// Delete ensures that system priority classes are not deleted.
func (r *REST) Delete(ctx genericapirequest.Context, name string, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
for _, spc := range scheduling.SystemPriorityClasses() {
if name == spc.Name {
return nil, false, apierrors.NewForbidden(scheduling.Resource("priorityclasses"), spc.Name, errors.New("this is a system priority class and cannot be deleted"))
}
}
return r.Store.Delete(ctx, name, options)
}

View File

@@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
@@ -105,6 +106,22 @@ func TestDelete(t *testing.T) {
test.TestDelete(validNewPriorityClass())
}
// TestDeleteSystemPriorityClass checks that system priority classes cannot be deleted.
func TestDeleteSystemPriorityClass(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
key := "test/system-node-critical"
ctx := genericapirequest.NewContext()
pc := scheduling.SystemPriorityClasses()[0]
if err := storage.Store.Storage.Create(ctx, key, pc, nil, 0); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if _, _, err := storage.Delete(ctx, pc.Name, nil); err == nil {
t.Error("expected to receive an error")
}
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)

View File

@@ -68,9 +68,7 @@ func (priorityClassStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate is the default update validation for an end user.
func (priorityClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidatePriorityClass(obj.(*scheduling.PriorityClass))
updateErrorList := validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass))
return append(validationErrorList, updateErrorList...)
return validation.ValidatePriorityClassUpdate(obj.(*scheduling.PriorityClass), old.(*scheduling.PriorityClass))
}
// AllowUnconditionalUpdate is the default update policy for PriorityClass objects.

View File

@@ -68,22 +68,6 @@ func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStart
}
func AddSystemPriorityClasses() genericapiserver.PostStartHookFunc {
priorityClasses := []*scheduling.PriorityClass{
{
ObjectMeta: metav1.ObjectMeta{
Name: scheduling.SystemNodeCritical,
},
Value: scheduling.SystemCriticalPriority + 1000,
Description: "Used for system critical pods that must not be moved from their current node.",
},
{
ObjectMeta: metav1.ObjectMeta{
Name: scheduling.SystemClusterCritical,
},
Value: scheduling.SystemCriticalPriority,
Description: "Used for system critical pods that must run in the cluster, but can be moved to another node if necessary.",
},
}
return func(hookContext genericapiserver.PostStartHookContext) error {
// Adding system priority classes is important. If they fail to add, many critical system
// components may fail and cluster may break.
@@ -94,7 +78,7 @@ func AddSystemPriorityClasses() genericapiserver.PostStartHookFunc {
return false, nil
}
for _, pc := range priorityClasses {
for _, pc := range scheduling.SystemPriorityClasses() {
_, err := schedClientSet.PriorityClasses().Get(pc.Name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
@@ -106,11 +90,12 @@ func AddSystemPriorityClasses() genericapiserver.PostStartHookFunc {
}
} else {
// Unable to get the priority class for reasons other than "not found".
glog.Warningf("unable to get PriorityClass %v: %v. Retrying...", pc.Name, err)
return false, err
}
}
}
glog.Infof("all system priority classes are created successfully.")
glog.Infof("all system priority classes are created successfully or already exist.")
return true, nil
})
// if we're never able to make it through initialization, kill the API server.