diff --git a/api/v1/runtimecomponent_types.go b/api/v1/runtimecomponent_types.go index 457779c71..c622da194 100644 --- a/api/v1/runtimecomponent_types.go +++ b/api/v1/runtimecomponent_types.go @@ -168,6 +168,10 @@ type RuntimeComponentSpec struct { // The list of hostnames and IPs that will be injected into the application pod's hosts file // +operator-sdk:csv:customresourcedefinitions:order=30,type=spec,displayName="Host Aliases" HostAliases []corev1.HostAlias `json:"hostAliases,omitempty"` + + // Name of the PriorityClass for the pod. + // +operator-sdk:csv:customresourcedefinitions:order=31,type=spec,displayName="Priority Class Name" + PriorityClassName *string `json:"priorityClassName,omitempty"` } // Defines the DNS @@ -1107,6 +1111,10 @@ func (cr *RuntimeComponent) GetHostAliases() []corev1.HostAlias { return cr.Spec.HostAliases } +func (cr *RuntimeComponent) GetPriorityClassName() *string { + return cr.Spec.PriorityClassName +} + // Initialize the RuntimeComponent instance func (cr *RuntimeComponent) Initialize() { if cr.Spec.PullPolicy == nil { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index c80cd3ef4..8b5ece930 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -693,6 +693,11 @@ func (in *RuntimeComponentSpec) DeepCopyInto(out *RuntimeComponentSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.PriorityClassName != nil { + in, out := &in.PriorityClassName, &out.PriorityClassName + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeComponentSpec. diff --git a/bundle/manifests/rc.app.stacks_runtimecomponents.yaml b/bundle/manifests/rc.app.stacks_runtimecomponents.yaml index 1407a9aec..0b3f49bc4 100644 --- a/bundle/manifests/rc.app.stacks_runtimecomponents.yaml +++ b/bundle/manifests/rc.app.stacks_runtimecomponents.yaml @@ -4590,6 +4590,9 @@ spec: is allowed from. type: object type: object + priorityClassName: + description: Name of the PriorityClass for the pod. + type: string probes: description: Define health checks on application container to determine whether it is alive or ready to receive traffic diff --git a/bundle/manifests/runtime-component.clusterserviceversion.yaml b/bundle/manifests/runtime-component.clusterserviceversion.yaml index 5c4543199..c6c7176d2 100644 --- a/bundle/manifests/runtime-component.clusterserviceversion.yaml +++ b/bundle/manifests/runtime-component.clusterserviceversion.yaml @@ -422,6 +422,11 @@ spec: application pod's hosts file displayName: Host Aliases path: hostAliases + - description: Name of the PriorityClass for the pod. + displayName: Priority Class Name + path: priorityClassName + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:text - description: Labels to set on ServiceMonitor. displayName: Monitoring Labels path: monitoring.labels diff --git a/common/types.go b/common/types.go index 60dd266df..5a3b31b5e 100644 --- a/common/types.go +++ b/common/types.go @@ -264,4 +264,5 @@ type BaseComponent interface { GetDNS() BaseComponentDNS GetDisableTopologyRouting() *bool GetHostAliases() []corev1.HostAlias + GetPriorityClassName() *string } diff --git a/config/crd/bases/rc.app.stacks_runtimecomponents.yaml b/config/crd/bases/rc.app.stacks_runtimecomponents.yaml index e2a9489a2..1dc093f9f 100644 --- a/config/crd/bases/rc.app.stacks_runtimecomponents.yaml +++ b/config/crd/bases/rc.app.stacks_runtimecomponents.yaml @@ -4586,6 +4586,9 @@ spec: is allowed from. type: object type: object + priorityClassName: + description: Name of the PriorityClass for the pod. + type: string probes: description: Define health checks on application container to determine whether it is alive or ready to receive traffic diff --git a/config/manifests/bases/runtime-component.clusterserviceversion.yaml b/config/manifests/bases/runtime-component.clusterserviceversion.yaml index 4f62bbf5e..dfe4ce485 100644 --- a/config/manifests/bases/runtime-component.clusterserviceversion.yaml +++ b/config/manifests/bases/runtime-component.clusterserviceversion.yaml @@ -367,6 +367,9 @@ spec: path: monitoring.endpoints x-descriptors: - urn:alm:descriptor:com.tectonic.ui:endpointList + - description: Name of the PriorityClass for the pod. + displayName: Priority Class Name + path: priorityClassName - description: Tolerations to be added to application pods. Tolerations allow the scheduler to schedule pods on nodes with matching taints. displayName: Tolerations diff --git a/doc/user-guide-v1.adoc b/doc/user-guide-v1.adoc index 735e215ad..5b3996858 100755 --- a/doc/user-guide-v1.adoc +++ b/doc/user-guide-v1.adoc @@ -152,6 +152,7 @@ Each `RuntimeComponent` CR must at least specify the `.spec.applicationImage` fi | `networkPolicy.disable` | A Boolean to disable the creation of the network policy. The default value is `false`. By default, network policies for an application are created and limit incoming traffic. | `networkPolicy.fromLabels` | The labels of one or more pods from which incoming traffic is allowed. | `networkPolicy.namespaceLabels` | The labels of namespaces from which incoming traffic is allowed. +| `priorityClassName` | The name of the PriorityClass to assign to the application pod. PriorityClasses define the scheduling priority and preemption behaviour of pods. For examples, see link:++https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/++[Pod Priority and Preemption]. | `probes` | Defines health checks on an application container to determine whether it is alive or ready to receive traffic. For examples, see link:++https://github.com/OpenLiberty/open-liberty-operator/blob/main/doc/user-guide-v1.adoc#configure-probes++[Configure probes]. | `probes.liveness` | A YAML object configuring the link:++https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request++[Kubernetes liveness probe] that controls when Kubernetes needs to restart the pod. | `probes.readiness` | A YAML object configuring the link:++https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes++[Kubernetes readiness probe] that controls when the pod is ready to receive traffic. diff --git a/examples/priorityclass/01-priorityclass.yaml b/examples/priorityclass/01-priorityclass.yaml new file mode 100644 index 000000000..14182cee4 --- /dev/null +++ b/examples/priorityclass/01-priorityclass.yaml @@ -0,0 +1,7 @@ +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: service-critical +value: 1000000 +globalDefault: false +description: "Reserved for critical workloads. Pods with this class may preempt lower-priority pods during resource contention. Pods are Priority 0 by default." \ No newline at end of file diff --git a/examples/priorityclass/02-runtimecomponent.yaml b/examples/priorityclass/02-runtimecomponent.yaml new file mode 100644 index 000000000..289cd9711 --- /dev/null +++ b/examples/priorityclass/02-runtimecomponent.yaml @@ -0,0 +1,42 @@ +apiVersion: rc.app.stacks/v1 +kind: RuntimeComponent +metadata: + name: payment-service +spec: + applicationImage: icr.io/appcafe/open-liberty/samples/getting-started@sha256:80a28b6a71ec02369cc13f621e4c3cca0d63b1977be76e15dabbfba48411107f + manageTLS: true + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 200m + memory: 512Mi + replicas: 2 + priorityClassName: service-critical + service: + port: 9443 + serviceAccount: + mountToken: true + probes: + startup: + failureThreshold: 12 + periodSeconds: 5 + httpGet: + path: /health/started + port: 9443 + readiness: + httpGet: + path: /health/ready + port: 9443 + initialDelaySeconds: 1 + periodSeconds: 5 + failureThreshold: 24 + liveness: + httpGet: + path: /health/live + port: 9443 + initialDelaySeconds: 8 + periodSeconds: 5 + + diff --git a/internal/deploy/kubectl/runtime-component-crd.yaml b/internal/deploy/kubectl/runtime-component-crd.yaml index 741c46fa6..42636b780 100644 --- a/internal/deploy/kubectl/runtime-component-crd.yaml +++ b/internal/deploy/kubectl/runtime-component-crd.yaml @@ -4589,6 +4589,9 @@ spec: is allowed from. type: object type: object + priorityClassName: + description: Name of the PriorityClass for the pod. + type: string probes: description: Define health checks on application container to determine whether it is alive or ready to receive traffic diff --git a/internal/deploy/kustomize/daily/base/runtime-component-crd.yaml b/internal/deploy/kustomize/daily/base/runtime-component-crd.yaml index 741c46fa6..42636b780 100644 --- a/internal/deploy/kustomize/daily/base/runtime-component-crd.yaml +++ b/internal/deploy/kustomize/daily/base/runtime-component-crd.yaml @@ -4589,6 +4589,9 @@ spec: is allowed from. type: object type: object + priorityClassName: + description: Name of the PriorityClass for the pod. + type: string probes: description: Define health checks on application container to determine whether it is alive or ready to receive traffic diff --git a/utils/utils.go b/utils/utils.go index fe9e5d7bf..7b5fe3bd5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -765,6 +765,11 @@ func CustomizePodSpec(pts *corev1.PodTemplateSpec, ba common.BaseComponent) { } pts.Spec.Tolerations = ba.GetTolerations() + + pts.Spec.PriorityClassName = "" + if ba.GetPriorityClassName() != nil { + pts.Spec.PriorityClassName = *ba.GetPriorityClassName() + } } // Initialize an empty TopologySpreadConstraints list and optionally prefers scheduling across zones/hosts for pods with podMatchLabels @@ -1012,6 +1017,11 @@ func CustomizeKnativeService(ksvc *servingv1.Service, ba common.BaseComponent) { } else { ksvc.Spec.Template.Spec.AutomountServiceAccountToken = nil } + + ksvc.Spec.Template.Spec.PriorityClassName = "" + if ba.GetPriorityClassName() != nil { + ksvc.Spec.Template.Spec.PriorityClassName = *ba.GetPriorityClassName() + } } // CustomizeHPA for autoscaling/v2 diff --git a/utils/utils_test.go b/utils/utils_test.go index a3d58bb82..ade25f0e9 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -494,6 +494,40 @@ func TestCustomizePodSpecServiceLinks(t *testing.T) { verifyTests(testCPS, t) } +func TestCustomizePodSpecPriorityClassName(t *testing.T) { + logger := zap.New() + logf.SetLogger(logger) + + spec := appstacksv1.RuntimeComponentSpec{ + ApplicationImage: appImage, + Service: service, + } + + pts, runtime := &corev1.PodTemplateSpec{}, createRuntimeComponent(name, namespace, spec) + CustomizePodSpec(pts, runtime) + defaultPriorityClassName := pts.Spec.PriorityClassName + + priorityClassName := "high-priority" + spec.PriorityClassName = &priorityClassName + pts, runtime = &corev1.PodTemplateSpec{}, createRuntimeComponent(name, namespace, spec) + CustomizePodSpec(pts, runtime) + setPriorityClassName := pts.Spec.PriorityClassName + + spec.PriorityClassName = nil + pts = &corev1.PodTemplateSpec{} + pts.Spec.PriorityClassName = priorityClassName + runtime = createRuntimeComponent(name, namespace, spec) + CustomizePodSpec(pts, runtime) + clearPriorityClassName := pts.Spec.PriorityClassName + + testCPS := []Test{ + {"Default PriorityClassName (not set)", "", defaultPriorityClassName}, + {"Set PriorityClassName", priorityClassName, setPriorityClassName}, + {"Clearing PriorityClassName", "", clearPriorityClassName}, + } + verifyTests(testCPS, t) +} + func TestCustomizePersistence(t *testing.T) { logger := zap.New() logf.SetLogger(logger)