import { isRecord } from '@ankhorage/utility/object'; import { isNonEmptyString } from '@ankhorage/utility/string'; import type { InfraComputeSelection, InfraDeploymentSpec, InfraRuntimeSelection, } from '../types/infraManifest'; import type { InfraShape } from '../types/infraValidation'; import { INFRA_RUNTIME_COMPATIBILITY } from './constants'; import { infraFields } from './infraFields'; import { isInfraCredentialRef } from './isInfraCredentialRef'; import { isInfraShape } from './isInfraShape'; /*** Validate provider-specific config and the exact same compatibility map used by TypeScript. */ export function isInfraDeploymentSpec(value: unknown): value is InfraDeploymentSpec { if (!isInfraShape(value, { compute: isCompute, runtime: isRuntime }) || !isRecord(value)) return false; if (!isRecord(value.compute) || !isRecord(value.runtime)) return false; const compute = value.compute.provider; const runtime = value.runtime.provider; return Object.entries(INFRA_RUNTIME_COMPATIBILITY).some( ([id, providers]) => id === runtime && providers.some((provider) => provider === compute), ); } /*** Compute configuration is closed per provider; control-plane refs cannot be managed secrets. */ function isCompute(value: unknown): boolean { if (!isRecord(value)) return false; if (value.provider === 'local') return isInfraShape(value, { provider: (provider) => provider === 'local', workingDirectory: infraFields.optionalText, } satisfies InfraShape>); return isInfraShape(value, { provider: (provider) => provider === 'hetzner', location: isNonEmptyString, serverType: infraFields.optionalText, image: infraFields.optionalText, credentials: (credentials) => credentials === undefined || isInfraCredentialRef(credentials), ssh: (ssh) => ssh === undefined || isInfraShape(ssh, { user: infraFields.optionalText, port: infraFields.optionalPositiveInteger, credentials: (credentials) => credentials === undefined || isInfraCredentialRef(credentials), }), } satisfies InfraShape>); } /*** Runtime topology expresses host roles and never couples k3s to a compute vendor. */ function isRuntime(value: unknown): boolean { if (!isRecord(value)) return false; switch (value.provider) { case 'minikube': return isInfraShape(value, { provider: (provider) => provider === 'minikube', profile: infraFields.optionalText, driver: (driver) => driver === undefined || driver === 'docker' || driver === 'podman', cpus: infraFields.optionalPositiveInteger, memoryMiB: infraFields.optionalPositiveInteger, } satisfies InfraShape>); case 'k3s': return isInfraShape(value, { provider: (provider) => provider === 'k3s', version: infraFields.optionalText, topology: (topology) => topology === undefined || isInfraShape(topology, { servers: infraFields.positiveInteger, agents: infraFields.nonnegativeInteger, }), } satisfies InfraShape>); case 'docker-compose': return isInfraShape(value, { provider: (provider) => provider === 'docker-compose', projectName: infraFields.optionalText, } satisfies InfraShape>); default: return false; } }