521 lines
23 KiB
TypeScript
521 lines
23 KiB
TypeScript
// SPEC: PJ2026-01060305 AgentRun execution policy + PJ2026-01020108 cancel lifecycle draft-2026-06-25-p0.
|
|
// Renders AgentRun YAML lane policy into runtime manager environment.
|
|
import { createHash } from "node:crypto";
|
|
import type { AgentRunLaneSpec } from "./agentrun-lanes";
|
|
|
|
export interface AgentRunArtifactService {
|
|
readonly serviceId: string;
|
|
readonly image: string;
|
|
readonly digest: string;
|
|
readonly repositoryDigest: string;
|
|
readonly imageTag: string;
|
|
readonly artifactKind: string;
|
|
readonly status: string;
|
|
readonly envIdentity: string;
|
|
readonly envImage: string;
|
|
readonly envDigest: string;
|
|
readonly envRepositoryDigest: string;
|
|
readonly bootCommit: string;
|
|
readonly bootScript: string;
|
|
readonly provenance: Record<string, unknown>;
|
|
}
|
|
|
|
export interface AgentRunArtifactCatalog {
|
|
readonly lane: string;
|
|
readonly sourceBranch: string;
|
|
readonly gitopsBranch: string;
|
|
readonly sourceCommitId: string;
|
|
readonly summary: string;
|
|
readonly services: readonly AgentRunArtifactService[];
|
|
}
|
|
|
|
export interface AgentRunGitopsRenderInput {
|
|
readonly sourceCommit: string;
|
|
readonly image: AgentRunArtifactService;
|
|
}
|
|
|
|
export interface AgentRunRenderedFile {
|
|
readonly path: string;
|
|
readonly content: string;
|
|
}
|
|
|
|
export function renderAgentRunControlPlaneManifests(spec: AgentRunLaneSpec): readonly Record<string, unknown>[] {
|
|
return [
|
|
{ apiVersion: "v1", kind: "Namespace", metadata: { name: spec.ci.namespace } },
|
|
{
|
|
apiVersion: "v1",
|
|
kind: "ServiceAccount",
|
|
metadata: {
|
|
name: spec.ci.serviceAccountName,
|
|
namespace: spec.ci.namespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: {
|
|
name: spec.ci.serviceAccountName,
|
|
namespace: spec.ci.namespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
rules: [
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns", "taskruns"], verbs: ["get", "list", "watch", "create", "patch", "update"] },
|
|
{ apiGroups: [""], resources: ["pods", "pods/log", "secrets", "configmaps", "persistentvolumeclaims"], verbs: ["get", "list", "watch", "create", "patch", "update", "delete"] },
|
|
],
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: {
|
|
name: spec.ci.serviceAccountName,
|
|
namespace: spec.ci.namespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
subjects: [{ kind: "ServiceAccount", name: spec.ci.serviceAccountName }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: spec.ci.serviceAccountName },
|
|
},
|
|
agentRunPipelineManifest(spec),
|
|
agentRunArgoProjectManifest(spec),
|
|
agentRunArgoApplicationManifest(spec),
|
|
];
|
|
}
|
|
|
|
export function renderAgentRunGitopsFiles(spec: AgentRunLaneSpec, input: AgentRunGitopsRenderInput): readonly AgentRunRenderedFile[] {
|
|
const catalog = agentRunArtifactCatalog(spec, input.sourceCommit, input.image);
|
|
const source = {
|
|
lane: spec.version,
|
|
sourceCommit: input.sourceCommit,
|
|
generatedBy: "unidesk config/agentrun.yaml",
|
|
configSource: "config/agentrun.yaml",
|
|
};
|
|
return [
|
|
{ path: "source.json", content: `${JSON.stringify(source, null, 2)}\n` },
|
|
{ path: spec.deployment.artifactCatalogPath, content: `${JSON.stringify(catalog, null, 2)}\n` },
|
|
{ path: `${spec.deployment.gitopsRoot}/argocd/project.yaml`, content: yaml(agentRunArgoProjectManifest(spec)) },
|
|
{ path: `${spec.deployment.gitopsRoot}/argocd/${spec.deployment.argocd.applicationFile}`, content: yaml(agentRunArgoApplicationManifest(spec)) },
|
|
{ path: `${spec.deployment.gitopsRoot}/${spec.deployment.runtimeRenderDir}/kustomization.yaml`, content: yaml(agentRunKustomizationManifest(spec)) },
|
|
{ path: `${spec.deployment.gitopsRoot}/${spec.deployment.runtimeRenderDir}/namespace.yaml`, content: yaml(agentRunRuntimeNamespaceManifest(spec)) },
|
|
...(spec.deployment.localPostgres.enabled ? [{ path: `${spec.deployment.gitopsRoot}/${spec.deployment.runtimeRenderDir}/postgres.yaml`, content: yaml(agentRunPostgresManifest(spec)) }] : []),
|
|
{ path: `${spec.deployment.gitopsRoot}/${spec.deployment.runtimeRenderDir}/mgr.yaml`, content: yamlAll(agentRunManagerManifests(spec, input.sourceCommit, input.image)) },
|
|
{ path: `${spec.deployment.gitopsRoot}/${spec.deployment.runtimeRenderDir}/runner-rbac.yaml`, content: yamlAll(agentRunRunnerRbacManifests(spec)) },
|
|
];
|
|
}
|
|
|
|
export function placeholderAgentRunImage(spec: AgentRunLaneSpec, sourceCommit: string): AgentRunArtifactService {
|
|
const digest = `sha256:${"0".repeat(64)}`;
|
|
const image = `${spec.ci.registryPrefix}/agentrun-mgr-env:${sourceCommit}`;
|
|
return {
|
|
serviceId: "agentrun-mgr",
|
|
artifactKind: "env-reuse",
|
|
status: "placeholder",
|
|
image,
|
|
digest,
|
|
repositoryDigest: `${spec.ci.registryPrefix}/agentrun-mgr-env@${digest}`,
|
|
imageTag: sourceCommit,
|
|
envIdentity: sourceCommit,
|
|
envImage: image,
|
|
envDigest: digest,
|
|
envRepositoryDigest: `${spec.ci.registryPrefix}/agentrun-mgr-env@${digest}`,
|
|
bootCommit: sourceCommit,
|
|
bootScript: "deploy/runtime/boot/agentrun-boot.sh",
|
|
provenance: {
|
|
sourceCommitId: sourceCommit,
|
|
source: "placeholder",
|
|
valuesPrinted: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function agentRunImageArtifact(spec: AgentRunLaneSpec, input: {
|
|
sourceCommit: string;
|
|
envIdentity: string;
|
|
digest: string;
|
|
status: string;
|
|
}): AgentRunArtifactService {
|
|
const image = `${spec.ci.registryPrefix}/${spec.deployment.manager.imageBuild.repository}:${input.envIdentity}`;
|
|
return {
|
|
serviceId: "agentrun-mgr",
|
|
artifactKind: "env-reuse",
|
|
status: input.status,
|
|
image,
|
|
digest: input.digest,
|
|
repositoryDigest: `${spec.ci.registryPrefix}/${spec.deployment.manager.imageBuild.repository}@${input.digest}`,
|
|
imageTag: input.envIdentity,
|
|
envIdentity: input.envIdentity,
|
|
envImage: image,
|
|
envDigest: input.digest,
|
|
envRepositoryDigest: `${spec.ci.registryPrefix}/${spec.deployment.manager.imageBuild.repository}@${input.digest}`,
|
|
bootCommit: input.sourceCommit,
|
|
bootScript: "deploy/runtime/boot/agentrun-boot.sh",
|
|
provenance: {
|
|
sourceCommitId: input.sourceCommit,
|
|
source: "unidesk-yaml-only",
|
|
configSource: "config/agentrun.yaml",
|
|
valuesPrinted: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function renderedFilesDigest(files: readonly AgentRunRenderedFile[]): string {
|
|
const hash = createHash("sha256");
|
|
for (const file of [...files].sort((left, right) => left.path.localeCompare(right.path))) {
|
|
hash.update(file.path);
|
|
hash.update("\0");
|
|
hash.update(file.content);
|
|
hash.update("\0");
|
|
}
|
|
return `sha256:${hash.digest("hex")}`;
|
|
}
|
|
|
|
export function renderedObjectsDigest(objects: readonly Record<string, unknown>[]): string {
|
|
return `sha256:${createHash("sha256").update(yamlAll(objects)).digest("hex")}`;
|
|
}
|
|
|
|
function agentRunPipelineManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
apiVersion: "tekton.dev/v1",
|
|
kind: "Pipeline",
|
|
metadata: {
|
|
name: spec.ci.pipeline,
|
|
namespace: spec.ci.namespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
spec: {
|
|
params: [
|
|
{ name: "git-url", type: "string", default: spec.source.remote },
|
|
{ name: "git-read-url", type: "string", default: spec.gitMirror.readUrl },
|
|
{ name: "git-write-url", type: "string", default: spec.gitMirror.writeUrl },
|
|
{ name: "source-branch", type: "string", default: spec.source.branch },
|
|
{ name: "gitops-branch", type: "string", default: spec.gitops.branch },
|
|
{ name: "revision", type: "string" },
|
|
{ name: "registry-prefix", type: "string", default: spec.ci.registryPrefix },
|
|
{ name: "tools-image", type: "string", default: spec.ci.toolsImage },
|
|
],
|
|
workspaces: [{ name: "source" }, { name: "git-ssh" }],
|
|
tasks: [
|
|
gitopsSmokeTask(spec),
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
function gitopsSmokeTask(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
name: "render-smoke",
|
|
workspaces: [{ name: "source", workspace: "source" }],
|
|
taskSpec: {
|
|
params: [{ name: "revision" }, { name: "tools-image" }],
|
|
workspaces: [{ name: "source" }],
|
|
steps: [
|
|
{
|
|
name: "render-smoke",
|
|
image: "$(params.tools-image)",
|
|
script: [
|
|
"#!/bin/sh",
|
|
"set -eu",
|
|
"echo '{\"event\":\"agentrun-ci-render-smoke\",\"status\":\"placeholder\",\"reason\":\"unidesk-yaml-only-control-plane\",\"valuesPrinted\":false}'",
|
|
].join("\n"),
|
|
},
|
|
],
|
|
},
|
|
params: [
|
|
{ name: "revision", value: "$(params.revision)" },
|
|
{ name: "tools-image", value: "$(params.tools-image)" },
|
|
],
|
|
when: [{ input: spec.deployment.format, operator: "in", values: ["unidesk-yaml-only"] }],
|
|
};
|
|
}
|
|
|
|
function agentRunArgoProjectManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
apiVersion: "argoproj.io/v1alpha1",
|
|
kind: "AppProject",
|
|
metadata: {
|
|
name: spec.deployment.argocd.project,
|
|
namespace: spec.gitops.argoNamespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
spec: {
|
|
description: `AgentRun ${spec.version} GitOps lane`,
|
|
sourceRepos: [spec.gitops.repoURL, spec.source.remote],
|
|
destinations: [{ server: "https://kubernetes.default.svc", namespace: spec.runtime.namespace }],
|
|
clusterResourceWhitelist: [{ group: "", kind: "Namespace" }],
|
|
namespaceResourceWhitelist: [{ group: "*", kind: "*" }],
|
|
},
|
|
};
|
|
}
|
|
|
|
function agentRunArgoApplicationManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
apiVersion: "argoproj.io/v1alpha1",
|
|
kind: "Application",
|
|
metadata: {
|
|
name: spec.gitops.argoApplication,
|
|
namespace: spec.gitops.argoNamespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
spec: {
|
|
project: spec.deployment.argocd.project,
|
|
source: {
|
|
repoURL: spec.gitops.repoURL,
|
|
targetRevision: spec.gitops.branch,
|
|
path: spec.gitops.path,
|
|
},
|
|
destination: {
|
|
server: "https://kubernetes.default.svc",
|
|
namespace: spec.runtime.namespace,
|
|
},
|
|
syncPolicy: {
|
|
automated: { prune: false, selfHeal: true },
|
|
syncOptions: ["CreateNamespace=true", "ApplyOutOfSyncOnly=true"],
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
function agentRunKustomizationManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
apiVersion: "kustomize.config.k8s.io/v1beta1",
|
|
kind: "Kustomization",
|
|
resources: [
|
|
"namespace.yaml",
|
|
...(spec.deployment.localPostgres.enabled ? ["postgres.yaml"] : []),
|
|
"mgr.yaml",
|
|
"runner-rbac.yaml",
|
|
],
|
|
};
|
|
}
|
|
|
|
function agentRunRuntimeNamespaceManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
return {
|
|
apiVersion: "v1",
|
|
kind: "Namespace",
|
|
metadata: {
|
|
name: spec.runtime.namespace,
|
|
labels: agentRunLabels(spec),
|
|
},
|
|
};
|
|
}
|
|
|
|
function agentRunPostgresManifest(spec: AgentRunLaneSpec): Record<string, unknown> {
|
|
const localPostgres = spec.deployment.localPostgres;
|
|
if (!localPostgres.enabled || localPostgres.serviceName === null || localPostgres.image === null || localPostgres.storage === null || localPostgres.port === null || localPostgres.database === null || localPostgres.user === null) {
|
|
throw new Error(`localPostgres is enabled for ${spec.version} without renderable YAML fields`);
|
|
}
|
|
const name = localPostgres.serviceName;
|
|
const secretName = spec.database.secretRef.name;
|
|
return {
|
|
apiVersion: "v1",
|
|
kind: "List",
|
|
items: [
|
|
{
|
|
apiVersion: "v1",
|
|
kind: "Service",
|
|
metadata: { name, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
spec: { selector: { "app.kubernetes.io/name": name }, ports: [{ name: "postgres", port: localPostgres.port, targetPort: "postgres" }] },
|
|
},
|
|
{
|
|
apiVersion: "apps/v1",
|
|
kind: "StatefulSet",
|
|
metadata: { name, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
spec: {
|
|
serviceName: name,
|
|
replicas: 1,
|
|
selector: { matchLabels: { "app.kubernetes.io/name": name } },
|
|
template: {
|
|
metadata: { labels: { ...agentRunLabels(spec), "app.kubernetes.io/name": name } },
|
|
spec: {
|
|
containers: [
|
|
{
|
|
name: "postgres",
|
|
image: localPostgres.image,
|
|
ports: [{ name: "postgres", containerPort: localPostgres.port }],
|
|
env: [
|
|
{ name: "POSTGRES_DB", valueFrom: { secretKeyRef: { name: secretName, key: "POSTGRES_DB" } } },
|
|
{ name: "POSTGRES_USER", valueFrom: { secretKeyRef: { name: secretName, key: "POSTGRES_USER" } } },
|
|
{ name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name: secretName, key: "POSTGRES_PASSWORD" } } },
|
|
{ name: "PGDATA", value: "/var/lib/postgresql/data/pgdata" },
|
|
],
|
|
volumeMounts: [{ name: "data", mountPath: "/var/lib/postgresql/data" }],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
volumeClaimTemplates: [
|
|
{
|
|
metadata: { name: "data" },
|
|
spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: localPostgres.storage } } },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function agentRunManagerManifests(spec: AgentRunLaneSpec, sourceCommit: string, image: AgentRunArtifactService): readonly Record<string, unknown>[] {
|
|
const imageRef = image.envRepositoryDigest || image.repositoryDigest;
|
|
return [
|
|
{ apiVersion: "v1", kind: "ServiceAccount", metadata: { name: spec.deployment.manager.serviceAccount, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) } },
|
|
{
|
|
apiVersion: "v1",
|
|
kind: "Service",
|
|
metadata: { name: spec.runtime.managerService, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
spec: {
|
|
selector: { "app.kubernetes.io/name": spec.runtime.managerDeployment },
|
|
ports: [{ name: "http", port: spec.runtime.managerPort, targetPort: "http" }],
|
|
},
|
|
},
|
|
{
|
|
apiVersion: "apps/v1",
|
|
kind: "Deployment",
|
|
metadata: { name: spec.runtime.managerDeployment, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
spec: {
|
|
replicas: 1,
|
|
selector: { matchLabels: { "app.kubernetes.io/name": spec.runtime.managerDeployment } },
|
|
template: {
|
|
metadata: {
|
|
labels: { ...agentRunLabels(spec), "app.kubernetes.io/name": spec.runtime.managerDeployment },
|
|
annotations: {
|
|
"agentrun.pikastech.local/lane": spec.version,
|
|
"agentrun.pikastech.local/source-commit": sourceCommit,
|
|
"agentrun.pikastech.local/env-identity": image.envIdentity,
|
|
},
|
|
},
|
|
spec: {
|
|
serviceAccountName: spec.deployment.manager.serviceAccount,
|
|
containers: [
|
|
{
|
|
name: "mgr",
|
|
image: imageRef,
|
|
imagePullPolicy: "IfNotPresent",
|
|
ports: [{ name: "http", containerPort: 8080 }],
|
|
env: managerEnv(spec, sourceCommit, imageRef, image.envIdentity),
|
|
readinessProbe: { httpGet: { path: "/health/readiness", port: "http" } },
|
|
livenessProbe: { httpGet: { path: "/health/live", port: "http" } },
|
|
resources: spec.deployment.manager.resources,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: `${spec.deployment.manager.serviceAccount}-runner-job-controller`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
rules: [
|
|
{ apiGroups: ["batch"], resources: ["jobs"], verbs: ["create", "delete", "get", "list", "watch"] },
|
|
{ apiGroups: [""], resources: ["pods"], verbs: ["delete", "get", "list", "watch"] },
|
|
{ apiGroups: [""], resources: ["persistentvolumeclaims"], verbs: ["create", "get", "list", "watch", "delete"] },
|
|
],
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: `${spec.deployment.manager.serviceAccount}-runner-job-controller`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
subjects: [{ kind: "ServiceAccount", name: spec.deployment.manager.serviceAccount }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: `${spec.deployment.manager.serviceAccount}-runner-job-controller` },
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: `${spec.deployment.manager.serviceAccount}-provider-secret-manager`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
rules: [{ apiGroups: [""], resources: ["secrets"], verbs: ["create", "delete", "get", "list", "patch", "update"] }],
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: `${spec.deployment.manager.serviceAccount}-provider-secret-manager`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
subjects: [{ kind: "ServiceAccount", name: spec.deployment.manager.serviceAccount }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: `${spec.deployment.manager.serviceAccount}-provider-secret-manager` },
|
|
},
|
|
];
|
|
}
|
|
|
|
function managerEnv(spec: AgentRunLaneSpec, sourceCommit: string, imageRef: string, envIdentity: string): readonly Record<string, unknown>[] {
|
|
return [
|
|
{ name: "AGENTRUN_LANE", value: spec.version },
|
|
{ name: "DATABASE_URL", valueFrom: { secretKeyRef: spec.database.secretRef } },
|
|
{ name: "AGENTRUN_SOURCE_COMMIT", value: sourceCommit },
|
|
{ name: "AGENTRUN_BOOT_COMMIT", value: sourceCommit },
|
|
{ name: "AGENTRUN_BOOT_MODE", value: "mgr" },
|
|
{ name: "AGENTRUN_BOOT_REPO_URL", value: spec.deployment.manager.bootRepoUrl },
|
|
{ name: "AGENTRUN_ENV_IDENTITY", value: envIdentity },
|
|
{ name: "AGENTRUN_RUNTIME_NAMESPACE", value: spec.runtime.namespace },
|
|
{ name: "AGENTRUN_INTERNAL_MGR_URL", value: spec.runtime.internalBaseUrl },
|
|
{ name: "AGENTRUN_RUNNER_IMAGE", value: imageRef },
|
|
{ name: "AGENTRUN_RUNNER_SERVICE_ACCOUNT", value: spec.deployment.runner.serviceAccount },
|
|
{ name: "AGENTRUN_RUNNER_JOB_NAME_PREFIX", value: spec.deployment.runner.jobNamePrefix },
|
|
{ name: "AGENTRUN_RUNNER_IDLE_TIMEOUT_MS", value: String(spec.deployment.runner.idleTimeoutMs) },
|
|
{ name: "AGENTRUN_BACKEND_RETRY_MAX_ATTEMPTS", value: String(spec.deployment.runner.backendRetry.maxAttempts) },
|
|
{ name: "AGENTRUN_BACKEND_RETRY_INITIAL_BACKOFF_MS", value: String(spec.deployment.runner.backendRetry.initialBackoffMs) },
|
|
{ name: "AGENTRUN_BACKEND_RETRY_MAX_BACKOFF_MS", value: String(spec.deployment.runner.backendRetry.maxBackoffMs) },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_MAX_RUNNERS", value: String(spec.deployment.runner.retention.maxRunners) },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_CLEANUP_ORDER", value: spec.deployment.runner.retention.cleanupOrder },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_ACTIVE_HEARTBEAT_MAX_AGE_MS", value: String(spec.deployment.runner.retention.activeHeartbeatMaxAgeMs) },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_MATCH_LABELS_JSON", value: JSON.stringify(spec.deployment.runner.retention.selectors.matchLabels) },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_JOB_NAME_PREFIXES", value: spec.deployment.runner.retention.selectors.jobNamePrefixes.join(",") },
|
|
{ name: "AGENTRUN_RUNNER_RETENTION_AGE_BASED_CLEANUP_ENABLED", value: String(spec.deployment.runner.retention.ageBasedCleanup.enabled) },
|
|
...(spec.deployment.runner.retention.ageBasedCleanup.maxAgeHours === null ? [] : [{ name: "AGENTRUN_RUNNER_RETENTION_AGE_BASED_MAX_AGE_HOURS", value: String(spec.deployment.runner.retention.ageBasedCleanup.maxAgeHours) }]),
|
|
{ name: "AGENTRUN_CANCEL_DELIVERY_MODE", value: spec.deployment.runner.cancelLifecycle.deliveryMode },
|
|
{ name: "AGENTRUN_CANCEL_GRACEFUL_ABORT_MS", value: String(spec.deployment.runner.cancelLifecycle.gracefulAbortMs) },
|
|
{ name: "AGENTRUN_CANCEL_KILL_ESCALATION_MS", value: String(spec.deployment.runner.cancelLifecycle.killEscalationMs) },
|
|
{ name: "AGENTRUN_CANCEL_STALE_HEARTBEAT_FENCING_MS", value: String(spec.deployment.runner.cancelLifecycle.staleHeartbeatFencingMs) },
|
|
{ name: "AGENTRUN_CANCEL_LATE_WRITE_FENCING_ENABLED", value: String(spec.deployment.runner.cancelLifecycle.lateWriteFencing.enabled) },
|
|
{ name: "AGENTRUN_CANCEL_EVENT_STAGES", value: spec.deployment.runner.cancelLifecycle.eventStages.join(",") },
|
|
...(spec.deployment.runner.egressProxyUrl === null ? [] : [{ name: "AGENTRUN_RUNNER_EGRESS_PROXY_URL", value: spec.deployment.runner.egressProxyUrl }]),
|
|
...(spec.deployment.runner.noProxyExtra.length === 0 ? [] : [{ name: "AGENTRUN_RUNNER_NO_PROXY_EXTRA", value: spec.deployment.runner.noProxyExtra.join(",") }]),
|
|
{ name: "AGENTRUN_API_KEY", valueFrom: { secretKeyRef: spec.deployment.manager.apiKeySecretRef } },
|
|
...Object.entries(spec.deployment.manager.env).sort(([left], [right]) => left.localeCompare(right)).map(([name, value]) => ({ name, value })),
|
|
...(spec.deployment.manager.unideskSshEndpointEnv === null ? [] : [{ name: spec.deployment.manager.unideskSshEndpointEnv.name, value: spec.deployment.manager.unideskSshEndpointEnv.value }]),
|
|
];
|
|
}
|
|
|
|
function agentRunRunnerRbacManifests(spec: AgentRunLaneSpec): readonly Record<string, unknown>[] {
|
|
return [
|
|
{ apiVersion: "v1", kind: "ServiceAccount", metadata: { name: spec.deployment.runner.serviceAccount, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) } },
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: `${spec.deployment.runner.serviceAccount}-secret-reader`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
rules: [{ apiGroups: [""], resources: ["secrets"], verbs: ["get"] }],
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: `${spec.deployment.runner.serviceAccount}-secret-reader`, namespace: spec.runtime.namespace, labels: agentRunLabels(spec) },
|
|
subjects: [{ kind: "ServiceAccount", name: spec.deployment.runner.serviceAccount }],
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: `${spec.deployment.runner.serviceAccount}-secret-reader` },
|
|
},
|
|
];
|
|
}
|
|
|
|
function agentRunArtifactCatalog(spec: AgentRunLaneSpec, sourceCommit: string, image: AgentRunArtifactService): AgentRunArtifactCatalog {
|
|
return {
|
|
lane: spec.version,
|
|
sourceBranch: spec.source.branch,
|
|
gitopsBranch: spec.gitops.branch,
|
|
sourceCommitId: sourceCommit,
|
|
summary: image.status === "placeholder" ? "build=0 reuse=0 placeholder=1" : "build=1 reuse=0 placeholder=0",
|
|
services: [image],
|
|
};
|
|
}
|
|
|
|
function agentRunLabels(spec: AgentRunLaneSpec): Record<string, string> {
|
|
return {
|
|
"app.kubernetes.io/part-of": "agentrun",
|
|
"agentrun.pikastech.local/lane": spec.version,
|
|
"agentrun.pikastech.local/node": spec.nodeId,
|
|
};
|
|
}
|
|
|
|
function yaml(value: unknown): string {
|
|
return `${Bun.YAML.stringify(value).trim()}\n`;
|
|
}
|
|
|
|
function yamlAll(values: readonly unknown[]): string {
|
|
return `${values.map((value) => Bun.YAML.stringify(value).trim()).join("\n---\n")}\n`;
|
|
}
|