Skip to content

Commit

Permalink
fixup! feat(cli): build based on configuration
Browse files Browse the repository at this point in the history
shell
  • Loading branch information
tuler committed Oct 11, 2024
1 parent 86aa0d3 commit 2173cc9
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 139 deletions.
5 changes: 3 additions & 2 deletions apps/cli/src/builder/directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const build = async (
name,
"--readjustment",
extraSize,
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
Expand All @@ -42,15 +43,15 @@ export const build = async (
const compression = "lzo"; // make customizable? default is gzip
const command = "mksquashfs";
const args = [
name,
filename,
"-all-time",
"0",
"-all-root", // XXX: should we use this?
"-noappend",
"-comp",
compression,
"-no-progress",
name,
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
Expand Down
5 changes: 3 additions & 2 deletions apps/cli/src/builder/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ export const build = async (
const compression = "lzo"; // make customizable? default is gzip
const command = "mksquashfs";
const args = [
"-",
filename,
"-tar",
"-all-time",
"0",
Expand All @@ -131,12 +133,11 @@ export const build = async (
"-comp",
compression,
"-no-progress",
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
inputFile: tar,
inputFile: path.join(destination, tar),
});
break;
}
Expand Down
5 changes: 3 additions & 2 deletions apps/cli/src/builder/tar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const build = async (
const compression = "lzo"; // make customizable? default is gzip
const command = "mksquashfs";
const args = [
"-",
filename,
"-tar",
"-all-time",
"0",
Expand All @@ -36,12 +38,11 @@ export const build = async (
"-comp",
compression,
"-no-progress",
filename,
];
await execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
inputFile: tar,
inputFile: path.join(destination, tar),
});
break;
}
Expand Down
95 changes: 3 additions & 92 deletions apps/cli/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,8 @@ import {
buildNone,
buildTar,
} from "../builder/index.js";
import { Config, DriveConfig } from "../config.js";
import { execaDockerFallback } from "../exec.js";

type ImageInfo = {
cmd: string[];
entrypoint: string[];
env: string[];
workdir: string;
};

type DriveResult = ImageInfo | undefined | void;
import { DriveConfig, DriveResult } from "../config.js";
import { bootMachine } from "../machine.js";

const buildDrive = async (
name: string,
Expand All @@ -47,86 +38,6 @@ const buildDrive = async (
}
};

const bootMachine = async (
config: Config,
info: ImageInfo | undefined,
sdkImage: string,
destination: string,
) => {
const { machine } = config;
const { assertRollingTemplate, maxMCycle, noRollup, ramLength, ramImage } =
machine;

// list of environment variables of docker image
const env = info?.env ?? [];
const envs = env.map(
(variable) => `--append-entrypoint=export "${variable}"`,
);

// bootargs from config string array
const bootargs = machine.bootargs.map(
(arg) => `--append-bootargs="${arg}"`,
);

// entrypoint from config or image info (Docker ENTRYPOINT + CMD)
const entrypoint =
machine.entrypoint ?? // takes priority
(info ? [...info.entrypoint, ...info.cmd].join(" ") : undefined); // ENTRYPOINT and CMD as a space separated string

if (!entrypoint) {
throw new Error("Undefined machine entrypoint");
}

const flashDrives = Object.entries(config.drives).map(([label, drive]) => {
const { format, mount, shared, user } = drive;
// TODO: filename should be absolute dir inside docker container
const filename = `${label}.${format}`;
const vars = [`label:${label}`, `filename:${filename}`];
if (mount) {
vars.push(`mount:${mount}`);
}
if (user) {
vars.push(`user:${user}`);
}
if (shared) {
vars.push("shared");
}
// don't specify start and length
return `--flash-drive=${vars.join(",")}`;
});

// command to change working directory if WORKDIR is defined
const command = "cartesi-machine";
const args = [
...bootargs,
...envs,
...flashDrives,
`--ram-image=${ramImage}`,
`--ram-length=${ramLength}`,
"--final-hash",
"--store=image",
`--append-entrypoint=${entrypoint}`,
];
if (info?.workdir) {
args.push(`--append-init=WORKDIR="${info.workdir}"`);
}
if (noRollup) {
args.push("--no-rollup");
}
if (maxMCycle) {
args.push(`--max-mcycle=${maxMCycle.toString()}`);
}
if (assertRollingTemplate) {
args.push("--assert-rolling-template");
}

return execaDockerFallback(command, args, {
cwd: destination,
image: sdkImage,
stdio: "inherit",
});
};

export default class Build extends BaseCommand<typeof Build> {
static summary = "Build application.";

Expand Down Expand Up @@ -186,7 +97,7 @@ export default class Build extends BaseCommand<typeof Build> {
const snapshotPath = this.getContextPath("image");

// create machine snapshot
await bootMachine(config, imageInfo, config.sdk, destination);
await bootMachine(config, imageInfo, destination);

await fs.chmod(snapshotPath, 0o755);
}
Expand Down
86 changes: 45 additions & 41 deletions apps/cli/src/commands/shell.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Args, Flags } from "@oclif/core";
import { execa } from "execa";
import fs from "fs-extra";
import { lookpath } from "lookpath";
import path from "path";
import { BaseCommand } from "../baseCommand.js";
import { ImageInfo } from "../config.js";
import { bootMachine } from "../machine.js";

export default class Shell extends BaseCommand<typeof Shell> {
static description = "Start a shell in cartesi machine of application";
Expand All @@ -18,62 +18,66 @@ export default class Shell extends BaseCommand<typeof Shell> {
};

static flags = {
config: Flags.file({
char: "c",
default: "cartesi.toml",
summary: "path to the configuration file",
}),
"run-as-root": Flags.boolean({
description: "run as root user",
default: false,
}),
};

private async startShell(
ext2Path: string,
runAsRoot: boolean,
): Promise<void> {
const containerDir = "/mnt";
const bind = `${path.resolve(path.dirname(ext2Path))}:${containerDir}`;
const ext2 = path.join(containerDir, path.basename(ext2Path));
const ramSize = "128Mi";
const driveLabel = "root";
const sdkImage = "cartesi/sdk:0.10.0"; // XXX: how to resolve sdk version?
const args = [
"run",
"--interactive",
"--tty",
"--volume",
bind,
sdkImage,
"cartesi-machine",
`--ram-length=${ramSize}`,
"--append-bootargs=no4lvl",
`--flash-drive=label:${driveLabel},filename:${ext2}`,
];
public async run(): Promise<void> {
const { flags } = await this.parse(Shell);

// get application configuration from 'cartesi.toml'
const config = this.getApplicationConfig(flags.config);

// destination directory for image and intermediate files
const destination = path.resolve(this.getContextPath());

if (runAsRoot) {
args.push("--append-init=USER=root");
// check if all drives are built
for (const [name, drive] of Object.entries(config.drives)) {
const filename = `${name}.${drive.format}`;
const pathname = this.getContextPath(filename);
if (!fs.existsSync(pathname)) {
throw new Error(
`drive '${name}' not built, run '${this.config.bin} build'`,
);
}
}

// create shell entrypoint
const info: ImageInfo = {
cmd: [],
entrypoint: ["/bin/bash"],
env: [],
workdir: "/",
};

// start with interactive mode on
config.machine.interactive = true;

/* why this?
if (!(await lookpath("stty"))) {
args.push("-i");
} else {
args.push("-it");
}
*/

await execa("docker", [...args, "--", "/bin/bash"], {
stdio: "inherit",
});
}
// interactive mode can't have final hash
config.machine.finalHash = false;

public async run(): Promise<void> {
const { flags } = await this.parse(Shell);
// do not store machine in interactive mode
config.machine.store = undefined;

// use pre-existing image or build dapp image
const ext2Path = this.getContextPath("root.ext2");
if (!fs.existsSync(ext2Path)) {
throw new Error(
`machine not built, run '${this.config.bin} build'`,
);
}
// run as root if flag is set
config.machine.user = flags["run-as-root"] ? "root" : undefined;

// execute the machine and save snapshot
await this.startShell(ext2Path, flags["run-as-root"]);
// boot machine
await bootMachine(config, info, destination);
}
}
21 changes: 21 additions & 0 deletions apps/cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ const DEFAULT_SDK = "cartesi/sdk:0.10.0";
type Builder = "directory" | "docker" | "empty" | "none" | "tar";
type DriveFormat = "ext2" | "sqfs";

export type ImageInfo = {
cmd: string[];
entrypoint: string[];
env: string[];
workdir: string;
};

export type DriveResult = ImageInfo | undefined | void;

export type DirectoryDriveConfig = {
builder: "directory";
extraSize: number; // default is 0 (no extra size)
Expand Down Expand Up @@ -69,10 +78,14 @@ export type MachineConfig = {
assertRollingTemplate?: boolean; // default given by cartesi-machine
bootargs: string[];
entrypoint?: string;
finalHash: boolean;
interactive?: boolean; // default given by cartesi-machine
maxMCycle?: bigint; // default given by cartesi-machine
noRollup?: boolean; // default given by cartesi-machine
ramLength: string;
ramImage: string;
store?: string;
user?: string; // default given by cartesi-machine
};

export type Config = {
Expand Down Expand Up @@ -104,10 +117,14 @@ export const defaultMachineConfig = (): MachineConfig => ({
assertRollingTemplate: undefined,
bootargs: [],
entrypoint: undefined,
finalHash: true,
interactive: undefined,
maxMCycle: undefined,
noRollup: undefined,
ramLength: DEFAULT_RAM,
ramImage: defaultRamImage(),
store: "image",
user: undefined,
});

export const defaultConfig = (): Config => ({
Expand Down Expand Up @@ -275,10 +292,14 @@ const parseMachine = (value: TomlPrimitive): MachineConfig => {
toml["assert-rolling-template"],
),
bootargs: parseStringArray(toml.bootargs),
finalHash: parseBoolean(toml["final-hash"], true),
interactive: undefined,
maxMCycle: parseOptionalNumber(toml["max-mcycle"]),
noRollup: parseBoolean(toml["no-rollup"], false),
ramLength: parseString(toml["ram-length"], DEFAULT_RAM),
ramImage: parseString(toml["ram-image"], defaultRamImage()),
store: "image",
user: parseOptionalString(toml.user),
};
};

Expand Down
2 changes: 2 additions & 0 deletions apps/cli/src/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export const execaDockerFallback = async (
`${options.cwd}:/work`,
"--workdir",
"/work",
"--interactive",
"--tty",
"--user",
`${userInfo.uid}:${userInfo.gid}`,
];
Expand Down
Loading

0 comments on commit 2173cc9

Please sign in to comment.