export interface BuildJobManifestInput { namespace: string; jobName: string; adapterType: string; image: string; envSecretName: string; serviceAccountName: string; labels: Record; 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 { 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" } }, ], }, }, }, }; }