Files
paperclip/packages/plugins/sandbox-providers/kubernetes/src/pod-spec-builder.ts
T

95 lines
3.3 KiB
TypeScript

export interface BuildJobManifestInput {
namespace: string;
jobName: string;
adapterType: string;
image: string;
envSecretName: string;
serviceAccountName: string;
labels: Record<string, string>;
resources: {
requests?: { cpu?: string; memory?: string };
limits?: { cpu?: string; memory?: string };
};
runtimeClassName?: string;
activeDeadlineSec: number;
ttlSecondsAfterFinished: number;
imagePullSecrets?: string[];
}
export function buildJobManifest(input: BuildJobManifestInput): Record<string, unknown> {
const podLabels = {
...input.labels,
"paperclip.io/role": "agent",
};
return {
apiVersion: "batch/v1",
kind: "Job",
metadata: {
name: input.jobName,
namespace: input.namespace,
labels: { ...input.labels },
},
spec: {
backoffLimit: 0,
ttlSecondsAfterFinished: input.ttlSecondsAfterFinished,
activeDeadlineSeconds: input.activeDeadlineSec,
template: {
metadata: { labels: podLabels },
spec: {
serviceAccountName: input.serviceAccountName,
// Agent containers call back to paperclip-server via HTTPS egress;
// they never call the Kubernetes API, so mounting an SA token is
// unnecessary attack surface.
automountServiceAccountToken: false,
restartPolicy: "Never",
...(input.runtimeClassName ? { runtimeClassName: input.runtimeClassName } : {}),
...(input.imagePullSecrets && input.imagePullSecrets.length > 0
? { imagePullSecrets: input.imagePullSecrets.map((name) => ({ name })) }
: {}),
securityContext: {
runAsNonRoot: true,
runAsUser: 1000,
runAsGroup: 1000,
fsGroup: 1000,
fsGroupChangePolicy: "OnRootMismatch",
seccompProfile: { type: "RuntimeDefault" },
},
containers: [
{
name: "agent",
image: input.image,
imagePullPolicy: "IfNotPresent",
command: ["/usr/bin/tini", "--", "/usr/local/bin/paperclip-agent-shim"],
envFrom: [{ secretRef: { name: input.envSecretName } }],
securityContext: {
runAsNonRoot: true,
runAsUser: 1000,
runAsGroup: 1000,
readOnlyRootFilesystem: true,
allowPrivilegeEscalation: false,
capabilities: { drop: ["ALL"] },
},
resources: {
requests: input.resources.requests ?? { cpu: "250m", memory: "512Mi" },
limits: input.resources.limits ?? { cpu: "2", memory: "4Gi" },
},
volumeMounts: [
{ name: "workspace", mountPath: "/workspace" },
{ name: "home", mountPath: "/home/paperclip" },
{ name: "cache", mountPath: "/home/paperclip/.cache" },
{ name: "tmp", mountPath: "/tmp" },
],
},
],
volumes: [
{ name: "workspace", emptyDir: { sizeLimit: "8Gi" } },
{ name: "home", emptyDir: { sizeLimit: "1Gi" } },
{ name: "cache", emptyDir: { sizeLimit: "1Gi" } },
{ name: "tmp", emptyDir: { sizeLimit: "2Gi" } },
],
},
},
},
};
}