alwaysai
Version:
The alwaysAI command-line interface (CLI)
220 lines (205 loc) • 5.39 kB
text/typescript
import { CliTerseError } from '@alwaysai/alwayscli';
import * as winston from 'winston';
import { TargetHardware } from '../../core/app';
import { ALWAYSAI_SYSTEM_ID } from '../../environment';
import { logger } from '../logger';
import { Spawner } from '../spawner';
import { stringifyError } from '../stringify-error';
export async function buildDockerImage(props: {
targetHostSpawner: Spawner;
targetHardware?: string;
dockerImageTag?: string;
dockerfilePath?: string;
pullBaseImage?: boolean;
runInForeground?: boolean;
logger?: winston.Logger;
}) {
const {
targetHostSpawner,
targetHardware,
dockerImageTag,
dockerfilePath,
pullBaseImage,
runInForeground,
logger
} = props;
let args = ['build', '--network=host'];
if (targetHardware) {
args = args.concat(['--build-arg', `ALWAYSAI_HW=${targetHardware}`]);
}
if (dockerImageTag) {
args = args.concat(['-t', dockerImageTag]);
}
if (dockerfilePath) {
args = args.concat(['-f', dockerfilePath]);
}
if (pullBaseImage) {
args = args.concat(['--pull']);
}
// first run to see build logs
if (runInForeground) {
// On Windows, stream to stdout to prevent garbled log output
if (process.platform === 'win32') {
const dockerLogs = await targetHostSpawner.runStreaming({
exe: 'docker',
args: args.concat(['.']),
cwd: '.'
});
dockerLogs.pipe(process.stdout);
} else {
targetHostSpawner.runForegroundSync({
exe: 'docker',
args: args.concat(['.']),
cwd: '.'
});
}
} else {
// NOTE: Seeing errors with streaming logs to readable stream so sending all-at-once for now.
// See:
// * https://github.com/winstonjs/winston/issues/1339
// * https://github.com/sponja23/winston-stream-wrapper/blob/main/src/index.ts
const dockerLogs = await targetHostSpawner.run({
exe: 'docker',
args: args.concat(['.']),
cwd: '.'
});
logger?.debug(dockerLogs);
}
// second run returns an output - docker does not rebuild it but it returns the success message
const output = await targetHostSpawner.run({
exe: 'docker',
args: args.concat(['--quiet', '.']),
cwd: '.'
});
const dockerImageId = output.trim();
logger?.info(`Built ${dockerImageId}`);
return dockerImageId;
}
export async function pullDockerImage(props: {
targetHostSpawner: Spawner;
dockerImageId: string;
}) {
const { targetHostSpawner, dockerImageId } = props;
const dockerArgs: string[] = ['pull', dockerImageId];
try {
await targetHostSpawner.run({
exe: 'docker',
args: dockerArgs,
cwd: '.'
});
} catch (err) {
logger.error(stringifyError(err));
throw new CliTerseError(
`Pull access denied for ${dockerImageId}, repository does not exist or may require 'docker login'.`
);
}
}
export type DockerRunCmd = {
dockerImageId: string;
pullImage?: boolean;
remove?: boolean;
interactive?: boolean;
tty?: boolean;
volumes?: string[];
env_vars?: string[];
user?: string;
ports?: string[];
targetHardware?: TargetHardware;
workdir?: string;
detach?: boolean;
restart?: string;
exe?: string;
exeArgs?: string[];
};
export function getDockerRunCmd(cmd: DockerRunCmd) {
const args: string[] = ['run', '--privileged'];
if (ALWAYSAI_SYSTEM_ID) {
args.push('--env', `ALWAYSAI_SYSTEM_ID=${ALWAYSAI_SYSTEM_ID}`);
}
if (cmd.remove) {
args.push('--rm');
}
if (cmd.pullImage) {
args.push('--pull', 'always');
}
if (cmd.interactive) {
args.push('--interactive');
}
if (cmd.tty) {
args.push('--tty');
}
if (cmd.volumes) {
for (const v of cmd.volumes) {
args.push('--volume', v);
}
}
if (cmd.env_vars) {
for (const e of cmd.env_vars) {
args.push('--env', e);
}
}
if (cmd.user) {
args.push('--user', cmd.user);
}
if (cmd.ports) {
for (const p of cmd.ports) {
args.push('--publish', `127.0.0.1:${p}:${p}/tcp`);
}
} else {
args.push('--network=host');
}
if (
cmd.targetHardware?.includes('jetson') ||
cmd.targetHardware?.includes('x86-trt')
) {
args.push('--runtime=nvidia');
}
if (cmd.targetHardware?.includes('jetson')) {
args.push('--ipc=host');
args.push('--volume');
args.push('/tmp/argus_socket:/tmp/argus_socket');
}
if (cmd.workdir) {
args.push('--workdir', cmd.workdir);
}
if (cmd.detach) {
args.push('--detach');
}
if (cmd.restart) {
args.push('--restart', cmd.restart);
}
args.push(cmd.dockerImageId);
if (cmd.exe) {
args.push(cmd.exe);
if (cmd.exeArgs) {
args.push(...cmd.exeArgs);
}
}
return args;
}
export async function runDockerContainer(props: {
targetHostSpawner: Spawner;
cmd: DockerRunCmd;
}) {
const { targetHostSpawner, cmd } = props;
const dockerArgs = getDockerRunCmd(cmd);
const output = await targetHostSpawner.run({
exe: 'docker',
args: dockerArgs,
cwd: '.'
});
const containerId = output.trim();
return containerId;
}
export async function runDockerContainerForeground(props: {
targetHostSpawner: Spawner;
cmd: DockerRunCmd;
}) {
const { targetHostSpawner, cmd } = props;
const dockerArgs = getDockerRunCmd(cmd);
await targetHostSpawner.runForeground({
exe: 'docker',
args: dockerArgs,
cwd: '.'
});
}