Hedronite · Dev Lesson · Polyglot-Dev / Rust · Tue 2026-10-06

Rust typed Kubernetes Quantity FromStr, Ord, checked add, and a ResourceQuota fit check

1000m and 1 are the same amount. Parse to one unit before you compare.

Lesson Class: Dev (T1 · Rust touching K8s via k8s-openapi)
Language Idiom: newtype · FromStr · derived Ord · error enum · checked_add / try_fold
Verified: cargo test 8 passed · fixtures from a real k3s v1.34.12 API server
Lag rule: TRPL whole canon shipped at Duha
Paired Ops: AKS namespace guardrails + kube-rs census
Paired Cert: CKA ResourceQuota versus LimitRange
Canonical Before Compare
Milli-units in an i128, then derive the order.
Checked, never wrapped
checked_add returns None. No Add impl.
Effective request
max(apps + sidecars, peak init step) + overhead.
Strings order by bytes. Quantities order by value.

<!-- hal:authoritative:yaml -->

*1000m and 1 are the same amount. 999Mi is more than 1G. Parse to one unit before you compare.*

§I. Frame

k8s-openapi hands you Quantity(pub String). The struct holds the wire string and nothing else. Compare two of those strings and "500m" > "1" comes out true, because the byte 5 sorts after the byte 1. Half a CPU is less than one CPU. Strings order by bytes. Quantities order by value.

Recent K8s Dev took match guards on image references (10-03), an async kube-rs label selector (09-30), and drain preflight (09-27). Today's spine is new: a newtype with FromStr, an error enum, derived Ord, and checked arithmetic, then a quota fit check built on it. Crate: quota_quantity.

§II. Canonical Before Compare

Canonical Before Compare (named technique). Turn every spelling into one integer unit, then let the integer do the ordering and the sums.

The Kubernetes Quantity reference fixes the unit choice. A quantity may carry at most three decimal places, and finer values round up (0.1m becomes 1m). Magnitude is capped at 2^63 - 1. So milli-units in an i128 hold every legal value exactly:

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Quantity(i128); // milli-units: 500m -> 500, 2 -> 2_000, 1Ki -> 1_024_000

Derived Ord on a one-field tuple struct compares that field (TRPL Appendix C). Because the field is canonical, q("1000m") == q("1") holds, q("999Mi") > q("1G") holds, and sorting [1Gi, 512Mi, 1G] gives [512Mi, 1G, 1Gi]. Rendering follows the reference's canonical form: 1.5 prints as 1500m and 1.5Gi as 1536Mi.

§III. FromStr and the error enum

pub enum QuantityError { Empty, BadNumber(String), BadSuffix(String), OutOfRange(String) }

impl FromStr for Quantity {
    type Err = QuantityError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // sign, then digits with at most one '.', then the suffix
        // suffix -> (base, exponent): "m" -> (10, -3), "Gi" -> (2, 30), "e3" -> (10, 3)
        // milli = mantissa * factor * 1000 / 10^scale, rounded up; checked_mul at every step
    }
}

Four rules carry the parser:

  1. The reference grammar lists decimal suffixes m k M G T P E (powers of ten) and binary suffixes Ki Mi Gi Ti Pi Ei (powers of two). The apimachinery parser also accepts n and u, which metrics APIs use for CPU, so the crate accepts them too. E alone is exa. e3 or E-2 is an exponent.
  2. Every multiply is checked_mul. An overflow becomes OutOfRange, never a wrapped number.
  3. Any sub-milli remainder rounds up, matching the reference.
  4. A value past 2^63 - 1 base units is refused. The API server caps instead. A capped request inside a quota sum would report a wrong total, so the crate stops there.

TryFrom<&k8s_openapi::...::Quantity> delegates to parse, which is how wire values enter the type.

§IV. Checked add and the effective pod request

Quantity has no Add impl. It has checked_add returning Option<Quantity>, and checked_sum folds with try_fold so one overflow stops the whole sum.

Quota does not add every container. The effective pod request is:

  1. App containers summed, plus every sidecar (an init container with restartPolicy: Always).
  2. The peak init step: each classic init container plus the sidecars started before it.
  3. The larger of 1 and 2, plus spec.overhead.

Pods in Succeeded or Failed drop out. Pod-level spec.resources returns QuotaError::Unsupported instead of a guess.

The rule was checked against a real API server. In tenant-d, two-apps-one-init has apps at 100m + 200m and a 500m init, so it counts 500m. sidecar-then-init has a 100m sidecar, a 300m init, and a 200m app, so it counts max(300m, 400m) = 400m. A third pod requested 1500m, ran true, and completed. The quota read 2400m while it existed and 900m after it finished. The crate computes 900m and 512Mi, equal to status.used.

§V. The fit check

check(quota, pods, candidate) reads status.hard and status.used, keeps requests.cpu, requests.memory, cpu, memory, and asks whether used + candidate <= hard. Real fixtures, real output:

$ cargo run -q -- ../fixture/tenant-a-pods.json ../fixture/tenant-a-quota.json ../fixture/candidate-big2.json
quota tenant-a/compute-quota
  key                  used  computed   hard  candidate  verdict
  requests.cpu        1500m     1500m      2          1  EXCEEDS
  requests.memory    1536Mi    1536Mi    2Gi        1Gi  EXCEEDS
exit 2

The API server refused the same big2 pod with exceeded quota: compute-quota, requested: requests.cpu=1,requests.memory=1Gi, used: requests.cpu=1500m,requests.memory=1536Mi, limited: requests.cpu=2,requests.memory=2Gi. The crate and the server agree.

§VI. Tests

cargo test: 8 passed. Unit tests cover the suffix table (0.1m and 100n both round up to 1m), the error variants, value ordering (999Mi > 1G), overflow surfacing as None, and CPU versus binary rendering. Fixture tests cover tenant-a against big2, the tenant-d init/sidecar/terminal rules, and the refusal of pod-level resources.

§VII. Close

Canonical Before Compare: parse once into milli-units, derive the order, add only with checks. The Ops census flags near_hard from the same arithmetic in miniature. The Cert lesson shows the refusal this crate predicts.

Drill: add limits.cpu and limits.memory to metered_resource, sum container limits the same way, and assert that tenant-a's computed limits equal its status.used of 2 and 2Gi.

Related