Skip to content

Commit 0528728

Browse files
stubbiclaude
andauthored
feat(instance): add priorityClassName for the product pod (outage guard) (#100)
* feat(crd): perTenantQuota/perTenantLimitRange on K8sExecutionSpec + PAPERCLIP_K8S_QUOTA_* env Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(lint): behaviour -> behavior (misspell) in perTenantQuota doc comment * chore: regen CRDs to match behavior spelling fix * feat(instance): add spec.availability.priorityClassName for the product pod Lets the product StatefulSet carry a scheduling PriorityClass so it can preempt low-value pods instead of sitting Pending when the fixed worker pool is full (prod outage guard). Optional/additive; unset keeps the cluster default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: regenerate api-reference for priorityClassName field Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent f21a965 commit 0528728

5 files changed

Lines changed: 67 additions & 0 deletions

File tree

api/v1alpha1/paperclipinstance_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,12 @@ type AvailabilitySpec struct {
12081208
// +optional
12091209
Affinity *corev1.Affinity `json:"affinity,omitempty"`
12101210

1211+
// PriorityClassName sets the scheduling PriorityClass on the product pod so
1212+
// it can preempt lower-priority workloads instead of sitting Pending when the
1213+
// node pool is full. Leave empty for the cluster default priority.
1214+
// +optional
1215+
PriorityClassName string `json:"priorityClassName,omitempty"`
1216+
12111217
// TopologySpreadConstraints specifies topology spread constraints.
12121218
// +optional
12131219
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"`

charts/paperclip-operator/templates/crds/paperclip.inc_instances.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,12 @@ spec:
16341634
required:
16351635
- enabled
16361636
type: object
1637+
priorityClassName:
1638+
description: |-
1639+
PriorityClassName sets the scheduling PriorityClass on the product pod so
1640+
it can preempt lower-priority workloads instead of sitting Pending when the
1641+
node pool is full. Leave empty for the cluster default priority.
1642+
type: string
16371643
replicas:
16381644
default: 1
16391645
description: |-

config/crd/bases/paperclip.inc_instances.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,6 +1628,12 @@ spec:
16281628
required:
16291629
- enabled
16301630
type: object
1631+
priorityClassName:
1632+
description: |-
1633+
PriorityClassName sets the scheduling PriorityClass on the product pod so
1634+
it can preempt lower-priority workloads instead of sitting Pending when the
1635+
node pool is full. Leave empty for the cluster default priority.
1636+
type: string
16311637
replicas:
16321638
default: 1
16331639
description: |-

docs/api-reference.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ _Appears in:_
214214
| `nodeSelector` _object (keys:string, values:string)_ | NodeSelector specifies node selection constraints. | | Optional: \{\} <br /> |
215215
| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#toleration-v1-core) array_ | Tolerations specifies pod tolerations. | | Optional: \{\} <br /> |
216216
| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#affinity-v1-core)_ | Affinity specifies pod affinity rules. | | Optional: \{\} <br /> |
217+
| `priorityClassName` _string_ | PriorityClassName sets the scheduling PriorityClass on the product pod so<br />it can preempt lower-priority workloads instead of sitting Pending when the<br />node pool is full. Leave empty for the cluster default priority. | | Optional: \{\} <br /> |
217218
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#topologyspreadconstraint-v1-core) array_ | TopologySpreadConstraints specifies topology spread constraints. | | Optional: \{\} <br /> |
218219

219220

@@ -657,6 +658,8 @@ _Appears in:_
657658
| `egressAllowFQDNs` _string array_ | EgressAllowFQDNs is the list of fully-qualified domain names tenant agent<br />pods may reach (e.g. the LLM gateway and required APIs). Enforced exactly<br />only under EgressMode "cilium". Maps to PAPERCLIP_K8S_EGRESS_ALLOW_FQDNS<br />(comma-separated). | | Optional: \{\} <br /> |
658659
| `egressAllowCIDRs` _string array_ | EgressAllowCIDRs is the list of CIDR blocks tenant agent pods may reach, in<br />addition to (or as the standard-mode substitute for) the FQDN allow-list.<br />Maps to PAPERCLIP_K8S_EGRESS_ALLOW_CIDRS (comma-separated). | | Optional: \{\} <br /> |
659660
| `namespacePrefix` _string_ | NamespacePrefix is prepended to each derived per-tenant namespace name,<br />letting multiple instances share a cluster without namespace collisions.<br />Maps to PAPERCLIP_K8S_NAMESPACE_PREFIX. | | Optional: \{\} <br /> |
661+
| `perTenantQuota` _[TenantResourceQuota](#tenantresourcequota)_ | PerTenantQuota stamps a ResourceQuota on each per-tenant namespace, bounding total<br />CPU/memory/pods so one tenant cannot starve the shared sandbox pool. Absent = no quota<br />(today's behavior). Maps to PAPERCLIP_K8S_QUOTA_* env consumed by the sandbox plugin. | | Optional: \{\} <br /> |
662+
| `perTenantLimitRange` _[TenantLimitRange](#tenantlimitrange)_ | PerTenantLimitRange stamps a LimitRange on each per-tenant namespace (per-container<br />default + max ceiling). Absent = no LimitRange. Maps to PAPERCLIP_K8S_LIMITRANGE_* env. | | Optional: \{\} <br /> |
660663

661664

662665
#### LoggingSpec
@@ -1259,3 +1262,46 @@ _Appears in:_
12591262
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#resourcerequirements-v1-core)_ | Resources specifies compute resources for the Tailscale sidecar container. | | Optional: \{\} <br /> |
12601263

12611264

1265+
#### TenantLimitRange
1266+
1267+
1268+
1269+
TenantLimitRange mirrors a Container-type v1 LimitRange for a per-tenant namespace:
1270+
per-container default, defaultRequest, and max.
1271+
1272+
1273+
1274+
_Appears in:_
1275+
- [K8sExecutionSpec](#k8sexecutionspec)
1276+
1277+
| Field | Description | Default | Validation |
1278+
| --- | --- | --- | --- |
1279+
| `defaultCpu` _string_ | | | Required: \{\} <br /> |
1280+
| `defaultMemory` _string_ | | | Required: \{\} <br /> |
1281+
| `defaultRequestCpu` _string_ | | | Required: \{\} <br /> |
1282+
| `defaultRequestMemory` _string_ | | | Required: \{\} <br /> |
1283+
| `maxCpu` _string_ | | | Required: \{\} <br /> |
1284+
| `maxMemory` _string_ | | | Required: \{\} <br /> |
1285+
1286+
1287+
#### TenantResourceQuota
1288+
1289+
1290+
1291+
TenantResourceQuota mirrors the hard limits of a v1 ResourceQuota for a per-tenant
1292+
namespace. All values are Kubernetes quantity strings.
1293+
1294+
1295+
1296+
_Appears in:_
1297+
- [K8sExecutionSpec](#k8sexecutionspec)
1298+
1299+
| Field | Description | Default | Validation |
1300+
| --- | --- | --- | --- |
1301+
| `pods` _string_ | | | Required: \{\} <br /> |
1302+
| `requestsCpu` _string_ | | | Required: \{\} <br /> |
1303+
| `requestsMemory` _string_ | | | Required: \{\} <br /> |
1304+
| `limitsCpu` _string_ | | | Required: \{\} <br /> |
1305+
| `limitsMemory` _string_ | | | Required: \{\} <br /> |
1306+
1307+

internal/resources/podtemplate.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ func BuildServerPodTemplate(instance *paperclipv1alpha1.Instance, extraPodAnnota
6565
if instance.Spec.Availability.Affinity != nil {
6666
podSpec.Affinity = instance.Spec.Availability.Affinity
6767
}
68+
if instance.Spec.Availability.PriorityClassName != "" {
69+
podSpec.PriorityClassName = instance.Spec.Availability.PriorityClassName
70+
}
6871
if len(instance.Spec.Availability.TopologySpreadConstraints) > 0 {
6972
podSpec.TopologySpreadConstraints = instance.Spec.Availability.TopologySpreadConstraints
7073
}

0 commit comments

Comments
 (0)