Improve sandbox env, add shared dep support

This commit is contained in:
Radon Rosborough 2021-01-09 12:36:17 -08:00
parent 5752918068
commit 670fabe177
9 changed files with 142 additions and 11 deletions

View File

@ -60,7 +60,7 @@ pkg-clean:
pkg-build:
@: $${L} $${T}
cd $(BUILD)/src && pkg="$(PWD)/$(BUILD)/pkg" $(or $(CMD),../build.bash)
cd $(BUILD)/src && pkg="$(PWD)/$(BUILD)/pkg" src="$(PWD)/$(BUILD)/src" $(or $(CMD),../build.bash)
pkg-debug:
@: $${L} $${T}
@ -148,7 +148,8 @@ test:
node backend/test-runner.js $(F)
sandbox:
node backend/sandbox.js
@: $${L}
L=$(L) node backend/sandbox.js
### Fetch artifacts from registries

61
backend/sandbox.bash Normal file
View File

@ -0,0 +1,61 @@
# This script is sourced by Bash within 'make sandbox'.
if [[ -z "$L" ]]; then
echo 'environment variable unset: $L' >&2
exit 1
fi
cfg="$(< "/opt/riju/langs/$L.json")" || exit 1
function get {
jq -r ".$1" <<< "${cfg}"
}
function has {
get "$@" | grep -vq '^null$'
}
function daemon {
has daemon && eval "$(get daemon)"
}
function setup {
has setup && eval "$(get setup)"
}
function repl {
has repl && eval "$(get repl)"
}
function main {
: > "$(get main)"
has prefix && get prefix >> "$(get main)"
get template >> "$(get main)"
has suffix && get suffix >> "$(get main)"
}
function compile {
has compile && echo "$(get compile)" && eval "$(get compile)"
}
function run-only {
has run && echo "$(get run)" && eval "$(get run)"
}
function run {
compile && run-only
}
function format {
has format && echo "$(get format.run)" && eval "( $(get format.run) ) < $(get main)"
}
function lsp {
has lsp.setup && echo "$(get lsp.setup)" && eval "$(get lsp.setup)"
has lsp && echo "$(get lsp.start)" && eval "$(get lsp.start)"
}
if [[ -z "$NS" ]]; then
main
setup
fi

View File

@ -1,6 +1,8 @@
import { spawn } from "child_process";
import { promises as fs } from "fs";
import process from "process";
import { quote } from "shell-quote";
import { v4 as getUUID } from "uuid";
import { borrowUser } from "./users.js";
@ -21,10 +23,19 @@ function log(msg) {
}
async function main() {
const sandboxScript = await fs.readFile("backend/sandbox.bash", "utf-8");
const lang = process.env.L;
if (!lang) {
die("environment variable unset: $L");
}
const uuid = getUUID();
const { uid, returnUser } = await borrowUser(log);
await run(privilegedSetup({ uid, uuid }), log);
const args = privilegedSpawn({ uid, uuid }, ["bash"]);
const args = privilegedSpawn({ uid, uuid }, [
"bash",
"-c",
`exec env L='${lang}' bash --rcfile <(cat <<< ${quote([sandboxScript])})`,
]);
const proc = spawn(args[0], args.slice(1), {
stdio: "inherit",
});

View File

@ -37,10 +37,19 @@ async function getCreatedUsers() {
}
async function getActiveUsers() {
let dirents;
try {
dirents = await fs.readdir("/tmp/riju");
} catch (err) {
if (err.code === "ENOENT") {
return new Set();
}
throw err;
}
return new Set(
(
await Promise.all(
(await fs.readdir("/tmp/riju"))
dirents
.filter((name) => name.match(uuidRegexp))
.map((name) => fs.stat(`/tmp/riju/${name}`))
)

View File

@ -34,6 +34,7 @@ ripgrep
sudo
tmux
unzip
vim
wget
yarn

View File

@ -20,6 +20,14 @@ export async function getLangs() {
.map((lang) => path.parse(lang).name);
}
// Return a list of the IDs of all the configured shared dependencies.
// Each such ID can be passed to readSharedDepConfig.
export async function getSharedDeps() {
return (await fs.readdir("shared"))
.filter((lang) => lang.endsWith(".yaml"))
.map((lang) => path.parse(lang).name);
}
// Return a list of objects representing the packages to be built. See
// the function implementation for the full list of keys.
export async function getPackages() {
@ -36,6 +44,17 @@ export async function getPackages() {
});
}
}
for (const dep of await getSharedDeps()) {
const type = "shared";
const name = `riju-${type}-${lang}`;
packages.push({
lang,
type,
name,
buildScriptPath: `build/${type}/${lang}/build.bash`,
debPath: `build/${type}/${lang}/${name}.deb`,
});
}
return packages;
}
@ -71,3 +90,17 @@ export async function readLangConfig(lang) {
}
return fixupLangConfig(langConfig);
}
// Read the YAML config file for the shared dependency with the given
// string ID and return it as an object.
export async function readSharedDepConfig(lang) {
const langConfig = YAML.parse(
await fs.readFile(`shared/${lang}.yaml`, "utf-8")
);
if (langConfig.id !== lang) {
throw new Error(
`shared dependency config id ${langConfig.id} doesn't match expected ${lang}`
);
}
return fixupLangConfig(langConfig);
}

View File

@ -3,18 +3,22 @@ import process from "process";
import { Command } from "commander";
import YAML from "yaml";
import { readLangConfig } from "./config.js";
import { readLangConfig, readSharedDepConfig } from "./config.js";
// Given a language config object, return the text of a Bash script
// that will build the (unpacked) riju-lang-foo Debian package into
// ${pkg} when run in an appropriate environment. This is a package
// that will install the language interpreter/compiler and associated
// tools.
function makeLangScript(langConfig) {
//
// isShared (optional) truthy means to generate a shared dependency
// package riju-shared-foo rather than a language installation
// package.
function makeLangScript(langConfig, isShared) {
const {
id,
name,
install: { prepare, apt, pip, manual, deb },
install: { prepare, apt, riju, pip, manual, deb },
} = langConfig;
let parts = [];
parts.push(`\
@ -45,6 +49,9 @@ sudo apt-get install -y ${apt.join(" ")}`);
if (apt) {
depends = depends.concat(apt);
}
if (riju) {
depends = depends.concat(riju.map((name) => `riju-shared-${name}`));
}
if (deb) {
depends = depends.concat(
deb.map((fname) => `\$(dpkg-deb -f "${fname}" Depends)`)
@ -52,11 +59,13 @@ sudo apt-get install -y ${apt.join(" ")}`);
}
parts.push(`depends=(${depends.map((dep) => `"${dep}"`).join(" ")})`);
let debianControlData = `\
Package: riju-lang-${id}
Package: riju-${isShared ? "shared" : "lang"}-${id}
Version: \$(date +%s%3N)
Architecture: amd64
Maintainer: Radon Rosborough <radon.neon@gmail.com>
Description: The ${name} language packaged for Riju
Description: The ${name} ${
isShared ? "shared dependency" : "language"
} packaged for Riju
Depends: \$(IFS=,; echo "\${depends[*]}")
Riju-Script-Hash: \$(sha1sum "\$0" | awk '{ print \$1 }')`;
parts.push(`\
@ -107,7 +116,7 @@ EOF`);
// that installs tools used by multiple languages, and can be declared
// as a dependency.
function makeSharedScript(langConfig) {
throw new Error("shared script generation not implemented yet");
return makeLangScript(langConfig, true);
}
// Parse command-line arguments, run main functionality, and exit.
@ -129,7 +138,13 @@ async function main() {
console.error(`make-script.js: unsupported --type ${program.type}`);
process.exit(1);
}
console.log(scriptMaker(await readLangConfig(program.lang)));
console.log(
scriptMaker(
program.type === "shared"
? await readSharedDepConfig(program.lang)
: await readLangConfig(program.lang)
)
);
process.exit(0);
}