import { describe, expect, test } from "bun:test"; import { createBuildJob } from "../../server/build-job"; import { createBuildpacksJob, DEFAULT_BUILDPACKS_IMAGE, } from "../../server/buildpacks-job"; import type { BuildSpec } from "../../shared/build-protocol"; function spec(architecture: "arm64" | "amd64"): BuildSpec { return { architecture, image: "registry.example.com/kuber/demo-web:latest", context: "apps/web", dockerfile: "docker/Web.Dockerfile", target: "production", buildArgs: ["NODE_ENV=production"], workspace: `sha256:${"a".repeat(64)}`, }; } describe("BuildKit Job generation", () => { test("quotes multiple image names as one BuildKit image exporter", () => { const job = createBuildJob({ name: "build-multi", namespace: "default", spec: spec("amd64"), workspaceClaimName: "workspace", cacheImage: "registry.example.com/cache/demo-web", pushImage: "registry.example.com/kuber/demo-web:latest", pushImages: ["registry.example.com/kuber/demo-worker:latest"], }); expect((job.spec as any).template.spec.containers[0].args).toContain( '--output=type=image,"name=registry.example.com/kuber/demo-web:latest,registry.example.com/kuber/demo-worker:latest",push=true', ); }); test.each(["arm64", "amd64"] as const)( "generates a rootless %s job", (architecture) => { const job = createBuildJob({ name: `build-${architecture}`, namespace: "kuber-system", spec: spec(architecture), workspaceClaimName: "build-workspaces", workspaceSubPath: "snapshot", cacheImage: "registry.example.com/cache/demo-web", registrySecretName: "registry-auth", nodeSelector: { "kubernetes.io/arch": "wrong", pool: "builders" }, }); const jobSpec = job.spec as any; const pod = jobSpec.template.spec; const container = pod.containers[0]; expect(pod.nodeSelector["kubernetes.io/arch"]).toBe(architecture); expect(pod.nodeSelector.pool).toBe("builders"); if (architecture === "amd64") { expect(pod.tolerations).toEqual([ { key: "arch", operator: "Equal", value: "amd64", effect: "NoExecute", }, ]); } else { expect(pod.tolerations).toBeUndefined(); } expect(pod.automountServiceAccountToken).toBe(false); expect(pod.securityContext).toMatchObject({ runAsNonRoot: true, runAsUser: 1000, seccompProfile: { type: "Unconfined" }, }); expect(container.securityContext).toEqual({ runAsNonRoot: true, runAsUser: 1000, allowPrivilegeEscalation: true, seccompProfile: { type: "Unconfined" }, appArmorProfile: { type: "Unconfined" }, }); expect(container.env).toContainEqual({ name: "BUILDKITD_FLAGS", value: "--oci-worker-no-process-sandbox", }); expect(container.args).toContain(`--opt=platform=linux/${architecture}`); expect(container.args).toContain( "--import-cache=type=registry,ref=registry.example.com/cache/demo-web", ); expect(container.args).toContain( "--export-cache=type=registry,ref=registry.example.com/cache/demo-web,mode=max", ); expect(container.args).toContain( "--output=type=image,name=registry.example.com/kuber/demo-web:latest,push=true", ); expect(container.volumeMounts).toContainEqual({ name: "workspace", mountPath: "/workspace", readOnly: true, subPath: "snapshot", }); expect(pod.volumes).toContainEqual({ name: "registry-auth", secret: { secretName: "registry-auth", items: [{ key: ".dockerconfigjson", path: "config.json" }], }, }); expect( jobSpec.template.metadata.annotations[ "container.apparmor.security.beta.kubernetes.io/buildkit" ], ).toBe("unconfined"); }, ); test("merges caller tolerations with the amd64 placement toleration", () => { const amd64Toleration = { key: "arch", operator: "Equal", value: "amd64", effect: "NoExecute", }; const customToleration = { key: "workload", operator: "Equal", value: "build", effect: "NoSchedule", }; const job = createBuildJob({ name: "build-tolerations", namespace: "default", spec: spec("amd64"), workspaceClaimName: "workspace", cacheImage: "registry.example.com/cache/demo-web", tolerations: [customToleration, amd64Toleration], }); expect((job.spec as any).template.spec.tolerations).toEqual([ customToleration, amd64Toleration, ]); }); test("preserves caller tolerations for arm64 without adding architecture placement", () => { const customToleration = { key: "workload", operator: "Equal", value: "build", effect: "NoSchedule", }; const job = createBuildJob({ name: "build-arm64-tolerations", namespace: "default", spec: spec("arm64"), workspaceClaimName: "workspace", cacheImage: "registry.example.com/cache/demo-web", tolerations: [customToleration], }); expect((job.spec as any).template.spec.tolerations).toEqual([ customToleration, ]); }); test("uses pushImage for output when set", () => { const job = createBuildJob({ name: "build-push", namespace: "default", spec: spec("amd64"), workspaceClaimName: "workspace", cacheImage: "registry.example.com/cache/demo-web", pushImage: "internal.registry:5000/kuber/demo-web:latest", }); const container = (job.spec as any).template.spec.containers[0]; expect(container.args).toContain( "--output=type=image,name=internal.registry:5000/kuber/demo-web:latest,push=true", ); expect(container.args).not.toContainEqual( expect.stringContaining("--output=type=image,name=registry.example.com"), ); }); test("adds insecure flags when configured", () => { const job = createBuildJob({ name: "build-insecure", namespace: "default", spec: spec("amd64"), workspaceClaimName: "workspace", cacheImage: "internal.registry:5000/cache/demo-web", pushImage: "internal.registry:5000/kuber/demo-web:latest", pushRegistryInsecure: true, cacheRegistryInsecure: true, }); const container = (job.spec as any).template.spec.containers[0]; expect(container.args).toContain( "--import-cache=type=registry,ref=internal.registry:5000/cache/demo-web,registry.insecure=true", ); expect(container.args).toContain( "--export-cache=type=registry,ref=internal.registry:5000/cache/demo-web,mode=max,registry.insecure=true", ); expect(container.args).toContain( "--output=type=image,name=internal.registry:5000/kuber/demo-web:latest,push=true,registry.insecure=true", ); }); test("no insecure flags when not configured", () => { const job = createBuildJob({ name: "build-secure", namespace: "default", spec: spec("amd64"), workspaceClaimName: "workspace", cacheImage: "registry.example.com/cache/demo-web", }); const container = (job.spec as any).template.spec.containers[0]; for (const arg of container.args) { expect(arg).not.toContain("registry.insecure"); } }); test("rejects traversal and invalid Kubernetes names", () => { expect(() => createBuildJob({ name: "Invalid_Name", namespace: "default", spec: spec("arm64"), workspaceClaimName: "workspace", cacheImage: "cache", }), ).toThrow("DNS label"); expect(() => createBuildJob({ name: "valid", namespace: "default", spec: { ...spec("arm64"), context: "../outside" }, workspaceClaimName: "workspace", cacheImage: "cache", }), ).toThrow("safe workspace-relative"); expect(() => createBuildJob({ name: "valid", namespace: "default", spec: spec("arm64"), workspaceClaimName: "workspace", workspaceSubPath: "../outside", cacheImage: "cache", }), ).toThrow("Workspace subPath"); }); }); describe("Buildpacks Job generation", () => { test("mounts custom package separately, preserves builtins and passes explicit creator order", () => { const uri = "https://github.com/example/bun/releases/download/v1/buildpack.cnb"; const job = createBuildpacksJob({ ...options("arm64"), spec: { ...options("arm64").spec, buildpackUri: uri }, buildpackSubPath: "workspaces/job-buildpack", }); const pod = (job.spec as any).template.spec; const creator = pod.containers[0]; expect(creator.args).toContain("--buildpacks"); expect(creator.args).toContain("/custom-buildpacks"); expect(creator.args).toContain("--order"); expect(creator.args).toContain("/package/order.toml"); expect(creator.volumeMounts).toContainEqual({ name: "workspace", mountPath: "/package", subPath: "workspaces/job-buildpack", readOnly: true, }); expect(creator.volumeMounts).toContainEqual({ name: "custom-buildpacks", mountPath: "/custom-buildpacks", readOnly: true, }); expect(pod.initContainers[0].args[0]).toContain("/cnb/buildpacks/."); expect(pod.initContainers[1].name).toBe("copy-source"); expect( pod.initContainers[1].volumeMounts.some( (mount: any) => mount.mountPath === "/package", ), ).toBe(false); expect(() => createBuildpacksJob({ ...options(), spec: { ...options().spec, buildpackUri: uri }, }), ).toThrow(/staged/); expect(() => createBuildpacksJob({ ...options(), spec: { ...options().spec, buildpackUri: uri }, buildpackSubPath: "../outside", }), ).toThrow(/safe/); }); const options = (architecture: "amd64" | "arm64" = "amd64") => ({ name: "build-cnb", namespace: "builds", spec: { ...spec(architecture), builder: "buildpacks" as const, dockerfile: undefined, target: undefined, buildArgs: [], }, workspaceClaimName: "workspaces", workspaceSubPath: "snapshot", cacheImage: `registry.example.com/cache/demo-web-cnb-${architecture}`, registrySecretName: "registry-auth", nodeSelector: { pool: "builders", "kubernetes.io/arch": "wrong" }, }); test.each(["amd64", "arm64"] as const)( "uses pinned lifecycle and isolated writable directories on %s", (architecture) => { const job = createBuildpacksJob(options(architecture)); const pod = (job.spec as any).template.spec; const init = pod.initContainers[0]; const creator = pod.containers[0]; expect(creator.name).toBe("buildkit"); expect(init.image).toBe(DEFAULT_BUILDPACKS_IMAGE); expect(creator.image).toBe(DEFAULT_BUILDPACKS_IMAGE); expect(init.command).toEqual(["/bin/sh", "-ec"]); expect(init.args).toEqual([ 'cp -R "$1/." /cnb-app/ && chmod -R u+rwX /cnb-app', "--", "/workspace/apps/web", ]); expect(init.volumeMounts[0]).toEqual({ name: "workspace", mountPath: "/workspace", readOnly: true, subPath: "snapshot", }); expect(creator.command).toEqual(["/cnb/lifecycle/creator"]); expect(creator.args).toEqual([ "--app", "/cnb-app", "--layers", "/layers", "--platform", "/platform", "--cache-image", `registry.example.com/cache/demo-web-cnb-${architecture}`, "--uid", "1000", "--gid", "1000", "registry.example.com/kuber/demo-web:latest", ]); expect(creator.volumeMounts).not.toContainEqual( expect.objectContaining({ name: "workspace" }), ); for (const name of ["app", "layers", "cache", "platform", "tmp"]) expect(pod.volumes).toContainEqual({ name, emptyDir: {} }); expect(pod.securityContext).toMatchObject({ runAsUser: 1000, runAsGroup: 1000, fsGroup: 1000, seccompProfile: { type: "RuntimeDefault" }, }); expect(creator.securityContext.allowPrivilegeEscalation).toBe(false); expect(pod.automountServiceAccountToken).toBe(false); expect(pod.nodeSelector).toEqual({ pool: "builders", "kubernetes.io/arch": architecture, }); expect(pod.tolerations?.length ?? 0).toBe( architecture === "amd64" ? 1 : 0, ); expect(creator.env).toContainEqual({ name: "CNB_PLATFORM_API", value: "0.15", }); expect(creator.env).toContainEqual({ name: "DOCKER_CONFIG", value: "/docker-config", }); expect(creator.volumeMounts).toContainEqual({ name: "registry-auth", mountPath: "/docker-config", readOnly: true, }); expect(pod.volumes).toContainEqual({ name: "registry-auth", secret: { secretName: "registry-auth", items: [{ key: ".dockerconfigjson", path: "config.json" }], }, }); expect(pod.imagePullSecrets).toEqual([{ name: "registry-auth" }]); }, ); test("exports additional tags and scopes insecure registry flags", () => { const job = createBuildpacksJob({ ...options(), cacheImage: "cache.local:5000/demo-cnb-amd64", pushImage: "internal.local:5000/web:latest", pushImages: [ "internal.local:5000/worker:latest", "internal.local:5000/other:latest", ], pushRegistryInsecure: true, cacheRegistryInsecure: true, }); expect((job.spec as any).template.spec.containers[0].args).toEqual([ "--app", "/cnb-app", "--layers", "/layers", "--platform", "/platform", "--cache-image", "cache.local:5000/demo-cnb-amd64", "--uid", "1000", "--gid", "1000", "--insecure-registry", "internal.local:5000", "--insecure-registry", "cache.local:5000", "--tag", "internal.local:5000/worker:latest", "--tag", "internal.local:5000/other:latest", "internal.local:5000/web:latest", ]); }); test("rejects unpinned builders and incompatible options", () => { expect(() => createBuildpacksJob({ ...options(), pushImages: ["another.example.com/worker:latest"], }), ).toThrow("same registry"); expect(() => createBuildpacksJob({ ...options(), buildpacksImage: "heroku/builder:26", }), ).toThrow("digest-pinned"); expect(() => createBuildpacksJob({ ...options(), buildpacksImage: "heroku/builder@sha256:deadbeef", }), ).toThrow("digest-pinned"); expect(() => createBuildpacksJob({ ...options(), spec: { ...options().spec, dockerfile: "Dockerfile" }, }), ).toThrow("does not support"); expect(() => createBuildpacksJob({ ...options(), spec: { ...options().spec, context: "../escape" }, }), ).toThrow("safe workspace-relative"); }); });