UNPKG

alwaysai

Version:

The alwaysAI command-line interface (CLI)

322 lines (294 loc) 7.92 kB
import { join } from 'path'; import Ajv, { JSONSchemaType } from 'ajv'; import * as chalk from 'chalk'; import { CliTerseError, CLI_TERSE_ERROR } from '@alwaysai/alwayscli'; import { TargetProtocol } from './target-protocol'; import { ALWAYSAI_CLI_EXECUTABLE_NAME } from '../../constants'; import { DockerSpawner, SshSpawner, JsSpawner, Spawner, SshDockerSpawner, logger, stringifyError } from '../../util'; import { TargetHardware, TARGET_HARDWARE_OPTIONS } from './get-target-hardware-type'; import { ConfigFileSchema, ConfigFileSchemaReturnType } from '@alwaysai/config-nodejs'; import { TARGET_JSON_FILE_NAME } from '../../paths'; export interface SshDockerTargetConfig { targetProtocol: 'ssh+docker:'; targetHostname: string; targetPath: string; dockerImageId: string; targetHardware: TargetHardware; deviceId: string; } export interface DockerTargetConfig { targetProtocol: 'docker:'; dockerImageId: string; targetHardware: TargetHardware; } export interface NativeTargetConfig { targetProtocol: 'native:'; } export type TargetConfig = | SshDockerTargetConfig | DockerTargetConfig | NativeTargetConfig; export const targetConfigSchema: JSONSchemaType<TargetConfig> = { anyOf: [ { type: 'object', properties: { targetProtocol: { type: 'string', const: 'ssh+docker:' }, targetHostname: { type: 'string' }, targetPath: { type: 'string' }, dockerImageId: { type: 'string' }, targetHardware: { type: 'string', enum: TARGET_HARDWARE_OPTIONS }, deviceId: { type: 'string' } }, required: [ 'targetProtocol', 'targetHostname', 'targetPath', 'dockerImageId', 'targetHardware', 'deviceId' ] }, { type: 'object', properties: { targetProtocol: { type: 'string', const: 'docker:' }, dockerImageId: { type: 'string' }, targetHardware: { type: 'string', enum: TARGET_HARDWARE_OPTIONS } }, required: ['targetProtocol', 'dockerImageId', 'targetHardware'] }, { type: 'object', properties: { targetProtocol: { type: 'string', const: 'native:' } }, required: ['targetProtocol'] } ] }; const ajv = new Ajv(); const validateFunction = ajv.compile(targetConfigSchema); const DID_YOU_RUN_APP_CONFIGURE = `Did you run "${ALWAYSAI_CLI_EXECUTABLE_NAME} app configure"?`; const ENOENT = { message: `${TARGET_JSON_FILE_NAME} not found. ${DID_YOU_RUN_APP_CONFIGURE}`, code: CLI_TERSE_ERROR }; export interface TargetJsonFileReturnType extends ConfigFileSchemaReturnType<TargetConfig> { name: string; readContainerSpawner: (opts?: { ignoreTargetHardware?: boolean; volumes?: string[]; env_vars?: string[]; }) => Spawner; readHostSpawner: () => Spawner; readTargetProtocolSafe: () => | 'ssh+docker:' | 'docker:' | 'native:' | undefined; readFieldSafe: (props: { name: string }) => string | undefined; describe: () => string | undefined; } export function TargetJsonFile(cwd = process.cwd()): TargetJsonFileReturnType { const filePath = join(cwd, TARGET_JSON_FILE_NAME); const configFile = ConfigFileSchema({ path: filePath, validateFunction, ENOENT }); return { ...configFile, name: TARGET_JSON_FILE_NAME, readContainerSpawner, readHostSpawner, readTargetProtocolSafe, readFieldSafe, describe }; function describe() { const config = configFile.readIfExists(); if (!config) { return `Target configuration file "${TARGET_JSON_FILE_NAME}" not found`; } const docker = chalk.bold('docker'); switch (config.targetProtocol) { case 'native:': { return `Target: Native alwaysAI Python on this host`; } case 'docker:': { return `Target: ${docker} container on this host`; } case 'ssh+docker:': { const hostname = chalk.bold(config.targetHostname); const path = chalk.bold(config.targetPath); return `Target: ${docker} container on ${hostname}, path ${path}`; } default: throw new Error('Unsupported protocol'); } } function readContainerSpawner(opts?: { ignoreTargetHardware?: boolean; volumes?: string[]; env_vars?: string[]; }) { const targetJson = configFile.read(); const volumes = opts?.volumes; const env_vars = opts?.env_vars; switch (targetJson.targetProtocol) { case 'ssh+docker:': { const { targetHostname, targetPath, dockerImageId, targetHardware } = targetJson; return SshDockerSpawner({ dockerImageId, targetPath, targetHostname, targetHardware, volumes, env_vars }); } case 'docker:': { const { dockerImageId } = targetJson; const targetHardware = opts && opts.ignoreTargetHardware ? undefined : targetJson.targetHardware; return DockerSpawner({ dockerImageId, targetHardware, volumes, env_vars }); } case 'native:': default: throw new CliTerseError('Unsupported protocol'); } } function readHostSpawner() { const targetJson = configFile.read(); switch (targetJson.targetProtocol) { case 'ssh+docker:': { const { targetPath, targetHostname } = targetJson; return SshSpawner({ targetHostname, targetPath }); } case 'docker:': case 'native:': { return JsSpawner(); } default: throw new CliTerseError('Unsupported protocol'); } } function readTargetProtocolSafe() { try { const targetJson = configFile.readIfExists(); if (!targetJson) { return undefined; } return targetJson.targetProtocol; } catch (err) { configFile.remove(); return undefined; } } function readFieldSafe(props: { name: string }) { try { const targetJson = configFile.readIfExists(); if (!targetJson) { return undefined; } switch (targetJson.targetProtocol) { case 'ssh+docker:': { switch (props.name) { case 'targetProtocol': return TargetProtocol[targetJson.targetProtocol]; case 'targetHostname': return targetJson.targetHostname; case 'targetPath': return targetJson.targetPath; case 'dockerImageId': return targetJson.dockerImageId; case 'targetHardware': return targetJson.targetHardware; case 'deviceId': return targetJson.deviceId; default: return undefined; } } case 'docker:': { switch (props.name) { case 'targetProtocol': return TargetProtocol[targetJson.targetProtocol]; case 'dockerImageId': return targetJson.dockerImageId; case 'targetHardware': return targetJson.targetHardware; default: return undefined; } } case 'native:': { switch (props.name) { case 'targetProtocol': return TargetProtocol[targetJson.targetProtocol]; default: return undefined; } } default: throw new CliTerseError('Unsupported protocol'); } } catch (err) { logger.error(stringifyError(err)); configFile.remove(); return undefined; } } }