Skip to content

Commit a36be18

Browse files
committed
feat(supervisor): optional image registry rewrite for run pods
1 parent 74db5a3 commit a36be18

4 files changed

Lines changed: 65 additions & 1 deletion

File tree

apps/supervisor/src/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ export const Env = z
185185
KUBERNETES_EPHEMERAL_STORAGE_SIZE_LIMIT: z.string().default("10Gi"),
186186
KUBERNETES_EPHEMERAL_STORAGE_SIZE_REQUEST: z.string().default("2Gi"),
187187
KUBERNETES_STRIP_IMAGE_DIGEST: BoolEnv.default(false),
188+
KUBERNETES_IMAGE_REGISTRY_REWRITE_FROM: z.string().optional(),
189+
KUBERNETES_IMAGE_REGISTRY_REWRITE_TO: z.string().optional(),
188190
KUBERNETES_CPU_REQUEST_MIN_CORES: z.coerce.number().min(0).default(0),
189191
KUBERNETES_CPU_REQUEST_RATIO: z.coerce.number().min(0).max(1).default(0.75), // Ratio of CPU limit, so 0.75 = 75% of CPU limit
190192
KUBERNETES_MEMORY_REQUEST_MIN_GB: z.coerce.number().min(0).default(0),
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { describe, expect, it } from "vitest";
2+
import { rewriteImageRegistry } from "./imageRegistry.js";
3+
4+
const FROM = "123456789012.dkr.ecr.us-east-1.amazonaws.com";
5+
const TO = "123456789012.dkr.ecr.eu-central-1.amazonaws.com";
6+
7+
describe("rewriteImageRegistry", () => {
8+
it("rewrites the registry host and keeps the rest of the reference", () => {
9+
expect(rewriteImageRegistry(`${FROM}/deployments/proj_abc:20260818.1`, FROM, TO)).toBe(
10+
`${TO}/deployments/proj_abc:20260818.1`
11+
);
12+
});
13+
14+
it("preserves a digest", () => {
15+
expect(rewriteImageRegistry(`${FROM}/deployments/proj_abc@sha256:abc123`, FROM, TO)).toBe(
16+
`${TO}/deployments/proj_abc@sha256:abc123`
17+
);
18+
});
19+
20+
it("is a no-op unless both ends are configured", () => {
21+
const ref = `${FROM}/deployments/proj_abc:tag`;
22+
23+
expect(rewriteImageRegistry(ref, undefined, TO)).toBe(ref);
24+
expect(rewriteImageRegistry(ref, FROM, undefined)).toBe(ref);
25+
expect(rewriteImageRegistry(ref, undefined, undefined)).toBe(ref);
26+
});
27+
28+
it("leaves other registries alone", () => {
29+
const ref = "ghcr.io/triggerdotdev/something:tag";
30+
expect(rewriteImageRegistry(ref, FROM, TO)).toBe(ref);
31+
});
32+
33+
it("only matches on a host boundary", () => {
34+
const lookalike = `${FROM}.evil.example.com/deployments/proj_abc:tag`;
35+
expect(rewriteImageRegistry(lookalike, FROM, TO)).toBe(lookalike);
36+
});
37+
38+
it("does not rewrite a host that merely contains the source", () => {
39+
const ref = `registry.example.com/${FROM}/proj_abc:tag`;
40+
expect(rewriteImageRegistry(ref, FROM, TO)).toBe(ref);
41+
});
42+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function rewriteImageRegistry(
2+
imageRef: string,
3+
from: string | undefined,
4+
to: string | undefined
5+
): string {
6+
if (!from || !to) {
7+
return imageRef;
8+
}
9+
10+
if (!imageRef.startsWith(`${from}/`)) {
11+
return imageRef;
12+
}
13+
14+
return `${to}${imageRef.slice(from.length)}`;
15+
}

apps/supervisor/src/workloadManager/kubernetes.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
withBlockIoUringSeccompProfile,
2121
withNodeSelector,
2222
} from "./kubernetesPodSpec.js";
23+
import { rewriteImageRegistry } from "./imageRegistry.js";
2324

2425
type ResourceQuantities = {
2526
[K in "cpu" | "memory" | "ephemeral-storage"]?: string;
@@ -160,7 +161,11 @@ export class KubernetesWorkloadManager implements WorkloadManager {
160161
containers: [
161162
{
162163
name: "run-controller",
163-
image: this.stripImageDigest(opts.image),
164+
image: rewriteImageRegistry(
165+
this.stripImageDigest(opts.image),
166+
env.KUBERNETES_IMAGE_REGISTRY_REWRITE_FROM,
167+
env.KUBERNETES_IMAGE_REGISTRY_REWRITE_TO
168+
),
164169
ports: [
165170
{
166171
containerPort: 8000,

0 commit comments

Comments
 (0)