feat: tooling api

This commit is contained in:
Endercheif 2023-03-15 22:30:13 -07:00
parent 7e09a3f9cd
commit 452b9d91d3
No known key found for this signature in database
GPG key ID: 7767459A0C8BEE00
7 changed files with 90 additions and 12 deletions

View file

@ -59,6 +59,7 @@ const SIGNALS = [
function get_job(body: RequestBody): Promise<Job> { function get_job(body: RequestBody): Promise<Job> {
let { let {
language, language,
tool,
version, version,
args, args,
stdin, stdin,
@ -159,6 +160,7 @@ function get_job(body: RequestBody): Promise<Job> {
run: run_memory_limit, run: run_memory_limit,
compile: compile_memory_limit, compile: compile_memory_limit,
}, },
tool
}) })
); );
}); });
@ -291,6 +293,22 @@ router.post('/execute', async (req, res) => {
} }
}); });
router.post('/tooling',async (req, res) => {
try {
const job = await get_job(req.body);
await job.prime();
const result = await job.analysis();
await job.cleanup();
return res.status(200).send(result);
} catch (error) {
return res.status(400).json(error)
}
})
router.get('/runtimes', (req, res) => { router.get('/runtimes', (req, res) => {
const runtimes = _runtimes.map(rt => { const runtimes = _runtimes.map(rt => {
return { return {
@ -298,6 +316,7 @@ router.get('/runtimes', (req, res) => {
version: rt.version.raw, version: rt.version.raw,
aliases: rt.aliases, aliases: rt.aliases,
runtime: rt.runtime, runtime: rt.runtime,
tooling: rt.tooling
}; };
}); });

View file

@ -6,6 +6,7 @@ import { spawn } from 'child_process';
import { join, relative, dirname } from 'node:path'; import { join, relative, dirname } from 'node:path';
import config from './config.js'; import config from './config.js';
import * as globals from './globals.js'; import * as globals from './globals.js';
import { type Runtime } from './runtime.js';
import { import {
mkdir, mkdir,
chown, chown,
@ -18,7 +19,7 @@ import { readdirSync, readFileSync } from 'node:fs';
import wait_pid from 'waitpid'; import wait_pid from 'waitpid';
import EventEmitter from 'events'; import EventEmitter from 'events';
import { File, ResponseBody } from './types.js'; import { File, LimitObject, ResponseBody } from './types.js';
const job_states = { const job_states = {
READY: Symbol('Ready to be primed'), READY: Symbol('Ready to be primed'),
@ -42,35 +43,40 @@ setInterval(() => {
export default class Job { export default class Job {
uuid: string; uuid: string;
logger: Logger; logger: Logger;
runtime: any; runtime: Runtime;
tool?: string;
files: File[]; files: File[];
args: string[]; args: string[];
stdin: string; stdin: string;
timeouts: { compile: number; run: number }; timeouts: LimitObject;
memory_limits: { compile: number; run: number }; memory_limits: LimitObject;
uid: number; uid: number;
gid: number; gid: number;
state: symbol; state: symbol;
dir: string; dir: string;
constructor({ constructor({
runtime, runtime,
tool,
files, files,
args, args,
stdin, stdin,
timeouts, timeouts,
memory_limits, memory_limits,
}: { }: {
runtime: unknown; runtime: Runtime;
tool?: string;
files: File[]; files: File[];
args: string[]; args: string[];
stdin: string; stdin: string;
timeouts: { compile: number; run: number }; timeouts: LimitObject;
memory_limits: { compile: number; run: number }; memory_limits: LimitObject;
}) { }) {
this.uuid = uuidv4(); this.uuid = uuidv4();
this.logger = create(`job/${this.uuid}`, {}); this.logger = create(`job/${this.uuid}`, {});
this.tool = tool;
this.runtime = runtime; this.runtime = runtime;
this.files = files.map((file: File, i: number) => ({ this.files = files.map((file: File, i: number) => ({
name: file.name || `file${i}.code`, name: file.name || `file${i}.code`,
@ -80,6 +86,7 @@ export default class Job {
: 'utf8', : 'utf8',
})); }));
this.args = args; this.args = args;
this.stdin = stdin; this.stdin = stdin;
@ -193,6 +200,7 @@ export default class Job {
env: { env: {
...this.runtime.env_vars, ...this.runtime.env_vars,
PISTON_LANGUAGE: this.runtime.language, PISTON_LANGUAGE: this.runtime.language,
PISTON_TOOLING: this.tool
}, },
stdio: 'pipe', stdio: 'pipe',
cwd: this.dir, cwd: this.dir,
@ -271,6 +279,45 @@ export default class Job {
}); });
} }
async analysis() {
if (this.state !== job_states.PRIMED) {
throw new Error(
'Job must be in primed state, current state: ' +
this.state.toString()
);
}
if (!this.runtime.tooling.includes(this.tool)) {
throw new Error(
`Tool ${this.tool} does not exist on ${this.runtime.toString()}`
)
}
this.logger.info(`Tooling job runtime=${this.runtime.toString()}; tooling=${this.tool}`);
const code_files =
(this.runtime.language === 'file' && this.files) ||
this.files.filter((file: File) => file.encoding == 'utf8');
const output = await this.safe_call(
join(this.runtime.pkgdir, 'tooling'),
code_files.map(x => x.name),
this.timeouts.run,
this.memory_limits.run
);
this.logger.debug('Analyzing')
return {
output,
language: this.runtime.language,
tool: this.tool,
version: this.runtime.version.raw
}
}
async execute() { async execute() {
if (this.state !== job_states.PRIMED) { if (this.state !== job_states.PRIMED) {
throw new Error( throw new Error(

View file

@ -12,6 +12,7 @@ export const runtimes: Runtime[] = [];
export class Runtime { export class Runtime {
language: string; language: string;
tooling: string[];
version: SemVer; version: SemVer;
aliases: string[]; aliases: string[];
pkgdir: string; pkgdir: string;
@ -26,10 +27,11 @@ export class Runtime {
_env_vars?: Record<string, any>; _env_vars?: Record<string, any>;
constructor(o: { constructor(o: {
language: string; language: string;
tooling?: string[];
version: SemVer; version: SemVer;
aliases: string[]; aliases: string[];
pkgdir: string; pkgdir: string;
runtime?: any; runtime?: string;
timeouts: { run: number; compile: number }; timeouts: { run: number; compile: number };
memory_limits: { run: number; compile: number }; memory_limits: { run: number; compile: number };
max_process_count: number; max_process_count: number;
@ -38,6 +40,7 @@ export class Runtime {
output_max_size: number; output_max_size: number;
}) { }) {
this.language = o.language; this.language = o.language;
this.tooling = o.tooling;
this.version = o.version; this.version = o.version;
this.aliases = o.aliases || []; this.aliases = o.aliases || [];
this.pkgdir = o.pkgdir; this.pkgdir = o.pkgdir;
@ -128,6 +131,7 @@ export class Runtime {
aliases, aliases,
provides, provides,
limit_overrides, limit_overrides,
tooling
} = info; } = info;
const version = parse(_version); const version = parse(_version);
@ -145,6 +149,7 @@ export class Runtime {
new Runtime({ new Runtime({
language: lang.language, language: lang.language,
aliases: lang.aliases, aliases: lang.aliases,
tooling,
version, version,
pkgdir: package_dir, pkgdir: package_dir,
runtime: language, runtime: language,
@ -160,6 +165,7 @@ export class Runtime {
new Runtime({ new Runtime({
language, language,
version, version,
tooling,
aliases, aliases,
pkgdir: package_dir, pkgdir: package_dir,
...Runtime.compute_all_limits(language, limit_overrides), ...Runtime.compute_all_limits(language, limit_overrides),

View file

@ -1,6 +1,7 @@
export interface Metadata { export interface Metadata {
language: string; language: string;
version: string; version: string;
tooling?: string[];
aliases?: string[]; aliases?: string[];
dependencies?: Record<string, string>; dependencies?: Record<string, string>;
provides: { provides: {
@ -23,6 +24,8 @@ export type Limit =
export type Limits = Record<Limit, number>; export type Limits = Record<Limit, number>;
export type LimitObject = { compile: number; run: number; };
export type LanguageMetadata = { export type LanguageMetadata = {
language: string; language: string;
version: string; version: string;
@ -37,6 +40,7 @@ export type File = {
}; };
export type RequestBody = { export type RequestBody = {
language: string; language: string;
tool?: string;
version: string; version: string;
files: Array<File>; files: Array<File>;
stdin?: string; stdin?: string;

1
packages/.gitignore vendored
View file

@ -6,3 +6,4 @@
!*/*/run !*/*/run
!*/*/compile !*/*/compile
!*/*/test.* !*/*/test.*
!*/*/tooling.*

View file

@ -18,4 +18,4 @@ cd ..
rm -rf build rm -rf build
bin/pip3 install numpy scipy pandas pycrypto whoosh bcrypt passlib sympy bin/pip3 install numpy scipy pandas pycrypto whoosh bcrypt passlib sympy flake8 mypy ruff

View file

@ -1,5 +1,6 @@
{ {
"language": "python", "language": "python",
"version": "3.10.0", "version": "3.10.0",
"aliases": ["py", "py3", "python3", "python3.10"] "aliases": ["py", "py3", "python3", "python3.10"],
"tooling": ["flake8", "mypy", "ruff"]
} }