Commit 91f893ee authored by Bobby Salamat's avatar Bobby Salamat Committed by Bobby (Babak) Salamat

Add priority to Kubernetes API

parent 53a5ee79
...@@ -19702,6 +19702,10 @@ ...@@ -19702,6 +19702,10 @@
"$ref": "v1.HostAlias" "$ref": "v1.HostAlias"
}, },
"description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods." "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods."
},
"priorityName": {
"type": "string",
"description": "If specified, indicates the pod's priority. `system` is a special keyword which indicates the highest priority. Any other name must be defined in Admission Controller config or pod will be rejected. If not specified, the pod priority will be zero."
} }
} }
}, },
...@@ -21001,6 +21005,11 @@ ...@@ -21001,6 +21005,11 @@
"qosClass": { "qosClass": {
"type": "string", "type": "string",
"description": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://github.com/kubernetes/kubernetes/blob/master/docs/design/resource-qos.md" "description": "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://github.com/kubernetes/kubernetes/blob/master/docs/design/resource-qos.md"
},
"priority": {
"type": "integer",
"format": "int32",
"description": "The priority value which is resolved by the Admission Controller from PodSpec.PriorityName. The higher the value, the higher the priority."
} }
} }
}, },
......
...@@ -2213,6 +2213,20 @@ type PodSpec struct { ...@@ -2213,6 +2213,20 @@ type PodSpec struct {
// file if specified. This is only valid for non-hostNetwork pods. // file if specified. This is only valid for non-hostNetwork pods.
// +optional // +optional
HostAliases []HostAlias HostAliases []HostAlias
// If specified, indicates the pod's priority. "SYSTEM" is a special keyword
// which indicates the highest priority. Any other name must be defined by
// creating a PriorityClass object with that name.
// If not specified, the pod priority will be default or zero if there is no
// default.
// +optional
PriorityClassName string
// The priority value. Various system components use this field to find the
// priority of the pod. When Priority Admission Controller is enabled, it
// prevents users from setting this field. The admission controller populates
// this field from PriorityClassName.
// The higher the value, the higher the priority.
// +optional
Priority *int32
} }
// HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the // HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the
......
...@@ -276,6 +276,10 @@ var ValidateClusterName = genericvalidation.ValidateClusterName ...@@ -276,6 +276,10 @@ var ValidateClusterName = genericvalidation.ValidateClusterName
// (where it should be) and this file. // (where it should be) and this file.
var ValidateClassName = NameIsDNSSubdomain var ValidateClassName = NameIsDNSSubdomain
// ValidatePiorityClassName can be used to check whether the given priority
// class name is valid.
var ValidatePriorityClassName = NameIsDNSSubdomain
// TODO update all references to these functions to point to the genericvalidation ones // TODO update all references to these functions to point to the genericvalidation ones
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain. // NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain(name string, prefix bool) []string { func NameIsDNSSubdomain(name string, prefix bool) []string {
...@@ -2296,6 +2300,20 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList { ...@@ -2296,6 +2300,20 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, ValidateHostAliases(spec.HostAliases, fldPath.Child("hostAliases"))...) allErrs = append(allErrs, ValidateHostAliases(spec.HostAliases, fldPath.Child("hostAliases"))...)
} }
if len(spec.PriorityClassName) > 0 {
if !utilfeature.DefaultFeatureGate.Enabled(features.PodPriority) {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("priorityClassName"), "Pod priority is disabled by feature-gate"))
} else {
for _, msg := range ValidatePriorityClassName(spec.PriorityClassName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityClassName"), spec.PriorityClassName, msg))
}
}
}
if spec.Priority != nil && !utilfeature.DefaultFeatureGate.Enabled(features.PodPriority) {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("priority"), "Pod priority is disabled by feature-gate"))
}
return allErrs return allErrs
} }
......
...@@ -3631,6 +3631,24 @@ func TestValidatePodSpec(t *testing.T) { ...@@ -3631,6 +3631,24 @@ func TestValidatePodSpec(t *testing.T) {
minGroupID := int64(0) minGroupID := int64(0)
maxGroupID := int64(2147483647) maxGroupID := int64(2147483647)
priorityEnabled := utilfeature.DefaultFeatureGate.Enabled("PodPriority")
defer func() {
var err error
// restoring the old value
if priorityEnabled {
err = utilfeature.DefaultFeatureGate.Set("PodPriority=true")
} else {
err = utilfeature.DefaultFeatureGate.Set("PodPriority=false")
}
if err != nil {
t.Errorf("Failed to restore feature gate for PodPriority: %v", err)
}
}()
err := utilfeature.DefaultFeatureGate.Set("PodPriority=true")
if err != nil {
t.Errorf("Failed to enable feature gate for PodPriority: %v", err)
return
}
successCases := []api.PodSpec{ successCases := []api.PodSpec{
{ // Populate basic fields, leave defaults for most. { // Populate basic fields, leave defaults for most.
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}}, Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
...@@ -3739,6 +3757,13 @@ func TestValidatePodSpec(t *testing.T) { ...@@ -3739,6 +3757,13 @@ func TestValidatePodSpec(t *testing.T) {
RestartPolicy: api.RestartPolicyAlways, RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
}, },
{ // Populate PriorityClassName.
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
PriorityClassName: "valid-name",
},
} }
for i := range successCases { for i := range successCases {
if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 { if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 {
...@@ -3909,12 +3934,47 @@ func TestValidatePodSpec(t *testing.T) { ...@@ -3909,12 +3934,47 @@ func TestValidatePodSpec(t *testing.T) {
RestartPolicy: api.RestartPolicyAlways, RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
}, },
"bad PriorityClassName": {
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
PriorityClassName: "InvalidName",
},
} }
for k, v := range failureCases { for k, v := range failureCases {
if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 { if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %q", k) t.Errorf("expected failure for %q", k)
} }
} }
err = utilfeature.DefaultFeatureGate.Set("PodPriority=false")
if err != nil {
t.Errorf("Failed to disable feature gate for PodPriority: %v", err)
return
}
priority := int32(100)
featuregatedCases := map[string]api.PodSpec{
"set PriorityClassName": {
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
PriorityClassName: "valid-name",
},
"set Priority": {
Volumes: []api.Volume{{Name: "vol", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Priority: &priority,
},
}
for k, v := range featuregatedCases {
if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure due to gated feature: %q", k)
}
}
} }
func extendPodSpecwithTolerations(in api.PodSpec, tolerations []api.Toleration) api.PodSpec { func extendPodSpecwithTolerations(in api.PodSpec, tolerations []api.Toleration) api.PodSpec {
...@@ -5781,6 +5841,50 @@ func TestValidatePodUpdate(t *testing.T) { ...@@ -5781,6 +5841,50 @@ func TestValidatePodUpdate(t *testing.T) {
"metadata.annotations[kubernetes.io/config.mirror]", "metadata.annotations[kubernetes.io/config.mirror]",
"changed mirror pod annotation", "changed mirror pod annotation",
}, },
{
api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
NodeName: "node1",
PriorityClassName: "bar-priority",
},
},
api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
NodeName: "node1",
PriorityClassName: "foo-priority",
},
},
"spec: Forbidden: pod updates",
"changed priority class name",
},
{
api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
NodeName: "node1",
PriorityClassName: "",
},
},
api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
NodeName: "node1",
PriorityClassName: "foo-priority",
},
},
"spec: Forbidden: pod updates",
"removed priority class name",
},
} }
for _, test := range tests { for _, test := range tests {
......
...@@ -114,6 +114,12 @@ const ( ...@@ -114,6 +114,12 @@ const (
// //
// Allows running a "debug container" in a pod namespaces to troubleshoot a running pod. // Allows running a "debug container" in a pod namespaces to troubleshoot a running pod.
DebugContainers utilfeature.Feature = "DebugContainers" DebugContainers utilfeature.Feature = "DebugContainers"
// owner: @bsalamat
// alpha: v1.8
//
// Add priority to pods. Priority affects scheduling and preemption of pods.
PodPriority utilfeature.Feature = "PodPriority"
) )
func init() { func init() {
...@@ -137,6 +143,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS ...@@ -137,6 +143,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
PersistentLocalVolumes: {Default: false, PreRelease: utilfeature.Alpha}, PersistentLocalVolumes: {Default: false, PreRelease: utilfeature.Alpha},
LocalStorageCapacityIsolation: {Default: false, PreRelease: utilfeature.Alpha}, LocalStorageCapacityIsolation: {Default: false, PreRelease: utilfeature.Alpha},
DebugContainers: {Default: false, PreRelease: utilfeature.Alpha}, DebugContainers: {Default: false, PreRelease: utilfeature.Alpha},
PodPriority: {Default: false, PreRelease: utilfeature.Alpha},
// inherited features from generic apiserver, relisted here to get a conflict if it is changed // inherited features from generic apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side: // unintentionally on either side:
......
...@@ -2520,6 +2520,20 @@ type PodSpec struct { ...@@ -2520,6 +2520,20 @@ type PodSpec struct {
// +patchMergeKey=ip // +patchMergeKey=ip
// +patchStrategy=merge // +patchStrategy=merge
HostAliases []HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"` HostAliases []HostAlias `json:"hostAliases,omitempty" patchStrategy:"merge" patchMergeKey:"ip" protobuf:"bytes,23,rep,name=hostAliases"`
// If specified, indicates the pod's priority. "SYSTEM" is a special keyword
// which indicates the highest priority. Any other name must be defined by
// creating a PriorityClass object with that name.
// If not specified, the pod priority will be default or zero if there is no
// default.
// +optional
PriorityClassName string `json:"priorityClassName,omitempty" protobuf:"bytes,24,opt,name=priorityClassName"`
// The priority value. Various system components use this field to find the
// priority of the pod. When Priority Admission Controller is enabled, it
// prevents users from setting this field. The admission controller populates
// this field from PriorityClassName.
// The higher the value, the higher the priority.
// +optional
Priority *int32 `json:"priority,omitempty" protobuf:"bytes,25,opt,name=priority"`
} }
// HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the // HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment