K8sQuantity.java
package io.github.baokhang83.mnemo.warden.agent.resize;
import java.math.BigDecimal;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parses a Kubernetes resource {@code Quantity} string into a byte count.
*
* <p>Exists because the API server normalizes memory quantities in {@code status}: a value
* {@link PodResizeClient} PATCHes as a plain byte count (e.g. {@code "209715200"}) comes back in
* {@code status.containerStatuses[].resources} as a different string for the same value (e.g.
* {@code "200Mi"}) — verified against a real target. Confirming a resize therefore requires
* parsing both sides to bytes and comparing numerically; string equality would never match, even
* on success.
*
* <p>Covers the suffixes the API server actually emits for memory (binary: Ki/Mi/Gi/Ti/Pi/Ei;
* decimal: k/M/G/T/P/E; or a bare integer) — not the full Quantity grammar (no exponential
* notation), since that's all {@link PodResizeClient} ever needs to read back.
*/
public final class K8sQuantity {
private static final Map<String, BigDecimal> BINARY_SUFFIXES =
Map.of(
"Ki", BigDecimal.valueOf(1024L),
"Mi", BigDecimal.valueOf(1024L * 1024),
"Gi", BigDecimal.valueOf(1024L * 1024 * 1024),
"Ti", BigDecimal.valueOf(1024L * 1024 * 1024 * 1024),
"Pi", BigDecimal.valueOf(1024L * 1024 * 1024 * 1024 * 1024),
"Ei", BigDecimal.valueOf(1024L * 1024 * 1024 * 1024 * 1024 * 1024));
private static final Map<String, BigDecimal> DECIMAL_SUFFIXES =
Map.of(
"k", BigDecimal.valueOf(1_000L),
"M", BigDecimal.valueOf(1_000_000L),
"G", BigDecimal.valueOf(1_000_000_000L),
"T", BigDecimal.valueOf(1_000_000_000_000L),
"P", BigDecimal.valueOf(1_000_000_000_000_000L),
"E", BigDecimal.valueOf(1_000_000_000_000_000_000L));
private static final Pattern QUANTITY = Pattern.compile("^(\\d+(?:\\.\\d+)?)([A-Za-z]*)$");
private K8sQuantity() {}
/** Parses a Quantity string (e.g. {@code "200Mi"}, {@code "1500000000"}) into a byte count. */
public static long parseBytes(String quantity) {
Matcher matcher = QUANTITY.matcher(quantity.trim());
if (!matcher.matches()) {
throw new IllegalArgumentException("not a recognized Quantity: " + quantity);
}
BigDecimal number = new BigDecimal(matcher.group(1));
String suffix = matcher.group(2);
if (suffix.isEmpty()) {
return number.longValueExact();
}
BigDecimal multiplier = BINARY_SUFFIXES.get(suffix);
if (multiplier == null) {
multiplier = DECIMAL_SUFFIXES.get(suffix);
}
if (multiplier == null) {
throw new IllegalArgumentException("unsupported Quantity suffix: " + suffix + " in " + quantity);
}
return number.multiply(multiplier).longValueExact();
}
}