Skip to content

Commit 201aaa1

Browse files
authored
Merge branch 'main' into rebase/upstream-2026-08-05
2 parents 5fa1fb0 + 9d742a4 commit 201aaa1

4 files changed

Lines changed: 39 additions & 2 deletions

File tree

packages/plugins/sandbox-providers/kubernetes/src/sandbox-cr-builder.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
* release path is explicit delete via sandboxCrOrchestrator.release().
1717
*/
1818

19+
import { TENANT_CONTAINER_MIN_RESOURCES } from "./utils.js";
20+
1921
// Where the seed init container mounts the home volume. Deliberately not
2022
// /home/paperclip: mounting there would shadow the image's baked home in the
2123
// init container too, leaving nothing to copy.
@@ -117,8 +119,11 @@ export function buildSandboxCrManifest(
117119
allowPrivilegeEscalation: false,
118120
capabilities: { drop: ["ALL"] },
119121
},
122+
// The tenant LimitRange rejects any container asking for less
123+
// than its floor, and a rejected init container means the pod is
124+
// never created at all.
120125
resources: {
121-
requests: { cpu: "50m", memory: "64Mi" },
126+
requests: { ...TENANT_CONTAINER_MIN_RESOURCES },
122127
limits: { cpu: "500m", memory: "256Mi" },
123128
},
124129
volumeMounts: [{ name: "home", mountPath: SEED_HOME_STAGING_PATH }],

packages/plugins/sandbox-providers/kubernetes/src/tenant-orchestrator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { KubeClients } from "./kube-client.js";
22
import { buildNetworkPolicyManifests } from "./network-policy.js";
33
import { buildCiliumNetworkPolicyManifest } from "./cilium-network-policy.js";
4+
import { TENANT_CONTAINER_MIN_RESOURCES } from "./utils.js";
45

56
export interface EnsureTenantInput {
67
namespace: string;
@@ -201,7 +202,7 @@ async function ensureLimitRange(clients: KubeClients, input: EnsureTenantInput):
201202
{
202203
type: "Container",
203204
max: { cpu: input.limitRange.maxCpu, memory: input.limitRange.maxMemory },
204-
min: { cpu: "100m", memory: "128Mi" },
205+
min: { ...TENANT_CONTAINER_MIN_RESOURCES },
205206
// The k8s client-node type names this `_default` but the actual
206207
// Kubernetes API field is `default`. We produce a JSON-shape
207208
// manifest so the cast is safe.

packages/plugins/sandbox-providers/kubernetes/src/utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,19 @@ export function paperclipLabels(input: LabelsInput): Record<string, string> {
5959
"paperclip.io/managed-by": "paperclip-k8s-plugin",
6060
};
6161
}
62+
63+
/**
64+
* The per-container resource floor the tenant LimitRange enforces
65+
* (`ensureLimitRange`, min for type `Container`). Every container this plugin
66+
* puts in a tenant namespace, init containers included, has to request at
67+
* least this much or the apiserver rejects the whole pod at admission.
68+
*
69+
* Shared rather than written out twice: a seed init container that asked for
70+
* less than the floor its own plugin installs took down every hosted run for
71+
* three days, and the two literals sitting in different files is what let the
72+
* drift happen silently.
73+
*/
74+
export const TENANT_CONTAINER_MIN_RESOURCES = {
75+
cpu: "100m",
76+
memory: "128Mi",
77+
} as const;

packages/plugins/sandbox-providers/kubernetes/test/unit/sandbox-cr-builder.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect } from "vitest";
22
import { buildSandboxCrManifest } from "../../src/sandbox-cr-builder.js";
3+
import { TENANT_CONTAINER_MIN_RESOURCES } from "../../src/utils.js";
34

45
const baseInput = {
56
namespace: "paperclip-acme",
@@ -181,6 +182,20 @@ describe("buildSandboxCrManifest: baked home seeding", () => {
181182
expect(seed.securityContext.capabilities.drop).toEqual(["ALL"]);
182183
});
183184

185+
// Every container in a tenant namespace is admitted against the LimitRange
186+
// this same plugin installs. An init container below that floor is not a
187+
// degraded pod, it is no pod at all: the apiserver refuses the create, the
188+
// Sandbox never materializes, and every exec the server tries afterwards
189+
// goes unanswered until the probe budget runs out. That is exactly how
190+
// hosted runs failed from 2026-08-04 to 2026-08-07, on every adapter, with
191+
// "the sandbox never answered the runtime probe" as the only symptom.
192+
it("requests at least the LimitRange floor for the seed container", () => {
193+
const cr = buildSandboxCrManifest(baseInput);
194+
const seed = cr.spec.podTemplate.spec.initContainers[0];
195+
expect(seed.resources.requests.cpu).toBe(TENANT_CONTAINER_MIN_RESOURCES.cpu);
196+
expect(seed.resources.requests.memory).toBe(TENANT_CONTAINER_MIN_RESOURCES.memory);
197+
});
198+
184199
it("does not let a seed failure block the run", () => {
185200
// An image with nothing baked into its home is the normal case for four of
186201
// the five harnesses. That must not be a CrashLoopBackOff.

0 commit comments

Comments
 (0)