Preliminary containerization work
This commit is contained in:
parent
83208355d4
commit
b99d17bcd3
4
Makefile
4
Makefile
|
@ -72,11 +72,13 @@ ifneq (,$(filter $(I),admin ci))
|
|||
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock -v $(HOME)/.aws:/var/riju/.aws -v $(HOME)/.docker:/var/riju/.docker -v $(HOME)/.ssh:/var/riju/.ssh -v $(HOME)/.terraform.d:/var/riju/.terraform.d -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e DOCKER_USERNAME -e DOCKER_PASSWORD -e DEPLOY_SSH_PRIVATE_KEY -e DOCKER_REPO -e S3_BUCKET -e DOMAIN -e VOLUME_MOUNT=$(VOLUME_MOUNT) $(SHELL_PORTS) $(SHELL_ENV) --network host riju:$(I) $(BASH_CMD)
|
||||
else ifeq ($(I),app)
|
||||
docker run -it --rm --hostname $(I) $(SHELL_PORTS) $(SHELL_ENV) riju:$(I) $(BASH_CMD)
|
||||
else ifneq (,$(filter $(I),runtime lang))
|
||||
else ifneq (,$(filter $(I),base lang))
|
||||
ifeq ($(I),lang)
|
||||
@: $${L}
|
||||
endif
|
||||
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src --label riju-install-target=yes $(SHELL_PORTS) $(SHELL_ENV) riju:$(LANG_TAG) $(BASH_CMD)
|
||||
else ifeq ($(I),runtime)
|
||||
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock $(SHELL_PORTS) $(SHELL_ENV) riju:$(I) $(BASH_CMD)
|
||||
else
|
||||
docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src $(SHELL_PORTS) $(SHELL_ENV) riju:$(I) $(BASH_CMD)
|
||||
endif
|
||||
|
|
|
@ -6,12 +6,10 @@ import pty from "node-pty";
|
|||
import pQueue from "p-queue";
|
||||
const PQueue = pQueue.default;
|
||||
import rpc from "vscode-jsonrpc";
|
||||
import { v4 as getUUID } from "uuid";
|
||||
|
||||
import { langs } from "./langs.js";
|
||||
import { borrowUser } from "./users.js";
|
||||
import * as util from "./util.js";
|
||||
import { bash } from "./util.js";
|
||||
import { bash, getUUID } from "./util.js";
|
||||
|
||||
const allSessions = new Set();
|
||||
|
||||
|
@ -24,16 +22,8 @@ export class Session {
|
|||
return langs[this.lang];
|
||||
}
|
||||
|
||||
get uid() {
|
||||
return this.uidInfo.uid;
|
||||
}
|
||||
|
||||
returnUser = async () => {
|
||||
this.uidInfo && (await this.uidInfo.returnUser());
|
||||
};
|
||||
|
||||
get context() {
|
||||
return { uid: this.uid, uuid: this.uuid };
|
||||
return { uuid: this.uuid, lang: this.lang };
|
||||
}
|
||||
|
||||
log = (msg) => this.logPrimitive(`[${this.uuid}] ${msg}`);
|
||||
|
@ -43,7 +33,7 @@ export class Session {
|
|||
this.uuid = getUUID();
|
||||
this.lang = lang;
|
||||
this.tearingDown = false;
|
||||
this.uidInfo = null;
|
||||
this.container = null;
|
||||
this.term = null;
|
||||
this.lsp = null;
|
||||
this.daemon = null;
|
||||
|
@ -57,24 +47,48 @@ export class Session {
|
|||
return await util.run(args, this.log, options);
|
||||
};
|
||||
|
||||
privilegedSetup = () => util.privilegedSetup(this.context);
|
||||
privilegedSpawn = (args) => util.privilegedSpawn(this.context, args);
|
||||
privilegedUseradd = () => util.privilegedUseradd(this.uid);
|
||||
privilegedTeardown = () => util.privilegedTeardown(this.context);
|
||||
privilegedSession = () => util.privilegedSession(this.context);
|
||||
privilegedWait = () => util.privilegedWait(this.context);
|
||||
privilegedExec = (args) => util.privilegedExec(this.context, args);
|
||||
|
||||
setup = async () => {
|
||||
try {
|
||||
allSessions.add(this);
|
||||
const { uid, returnUser } = await borrowUser();
|
||||
this.uidInfo = { uid, returnUser };
|
||||
this.log(`Borrowed uid ${this.uid}`);
|
||||
await this.run(this.privilegedSetup());
|
||||
const containerArgs = this.privilegedSession();
|
||||
const containerProc = spawn(containerArgs[0], containerArgs.slice(1));
|
||||
this.container = {
|
||||
proc: containerProc,
|
||||
};
|
||||
for (const stream of [containerProc.stdout, containerProc.stderr]) {
|
||||
stream.on("data", (data) =>
|
||||
this.send({
|
||||
event: "serviceLog",
|
||||
service: "container",
|
||||
output: data.toString("utf8"),
|
||||
})
|
||||
);
|
||||
containerProc.on("close", (code, signal) =>
|
||||
this.send({
|
||||
event: "serviceFailed",
|
||||
service: "container",
|
||||
error: `Exited with status ${signal || code}`,
|
||||
})
|
||||
);
|
||||
containerProc.on("error", (err) =>
|
||||
this.send({
|
||||
event: "serviceFailed",
|
||||
service: "container",
|
||||
error: `${err}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
await this.run(this.privilegedWait(this.context));
|
||||
if (this.config.setup) {
|
||||
await this.run(this.privilegedSpawn(bash(this.config.setup)));
|
||||
await this.run(this.privilegedExec(bash(this.config.setup)));
|
||||
}
|
||||
await this.runCode();
|
||||
if (this.config.daemon) {
|
||||
const daemonArgs = this.privilegedSpawn(bash(this.config.daemon));
|
||||
const daemonArgs = this.privilegedExec(bash(this.config.daemon));
|
||||
const daemonProc = spawn(daemonArgs[0], daemonArgs.slice(1));
|
||||
this.daemon = {
|
||||
proc: daemonProc,
|
||||
|
@ -105,9 +119,9 @@ export class Session {
|
|||
}
|
||||
if (this.config.lsp) {
|
||||
if (this.config.lsp.setup) {
|
||||
await this.run(this.privilegedSpawn(bash(this.config.lsp.setup)));
|
||||
await this.run(this.privilegedExec(bash(this.config.lsp.setup)));
|
||||
}
|
||||
const lspArgs = this.privilegedSpawn(bash(this.config.lsp.start));
|
||||
const lspArgs = this.privilegedExec(bash(this.config.lsp.start));
|
||||
const lspProc = spawn(lspArgs[0], lspArgs.slice(1));
|
||||
this.lsp = {
|
||||
proc: lspProc,
|
||||
|
@ -252,7 +266,7 @@ export class Session {
|
|||
writeCode = async (code) => {
|
||||
if (this.config.main.includes("/")) {
|
||||
await this.run(
|
||||
this.privilegedSpawn([
|
||||
this.privilegedExec([
|
||||
"mkdir",
|
||||
"-p",
|
||||
path.dirname(`${this.homedir}/${this.config.main}`),
|
||||
|
@ -260,7 +274,7 @@ export class Session {
|
|||
);
|
||||
}
|
||||
await this.run(
|
||||
this.privilegedSpawn([
|
||||
this.privilegedExec([
|
||||
"sh",
|
||||
"-c",
|
||||
`cat > ${path.resolve(this.homedir, this.config.main)}`,
|
||||
|
@ -283,7 +297,7 @@ export class Session {
|
|||
} = this.config;
|
||||
if (this.term) {
|
||||
const pid = this.term.pty.pid;
|
||||
const args = this.privilegedSpawn(
|
||||
const args = this.privilegedExec(
|
||||
bash(`kill -SIGTERM ${pid}; sleep 1; kill -SIGKILL ${pid}`)
|
||||
);
|
||||
spawn(args[0], args.slice(1));
|
||||
|
@ -310,7 +324,7 @@ export class Session {
|
|||
code += suffix + "\n";
|
||||
}
|
||||
await this.writeCode(code);
|
||||
const termArgs = this.privilegedSpawn(bash(cmdline));
|
||||
const termArgs = this.privilegedExec(bash(cmdline));
|
||||
const term = {
|
||||
pty: pty.spawn(termArgs[0], termArgs.slice(1), {
|
||||
name: "xterm-color",
|
||||
|
@ -349,14 +363,14 @@ export class Session {
|
|||
}
|
||||
if (this.formatter) {
|
||||
const pid = this.formatter.proc.pid;
|
||||
const args = this.privilegedSpawn(
|
||||
const args = this.privilegedExec(
|
||||
bash(`kill -SIGTERM ${pid}; sleep 1; kill -SIGKILL ${pid}`)
|
||||
);
|
||||
spawn(args[0], args.slice(1));
|
||||
this.formatter.live = false;
|
||||
this.formatter = null;
|
||||
}
|
||||
const args = this.privilegedSpawn(bash(this.config.format.run));
|
||||
const args = this.privilegedExec(bash(this.config.format.run));
|
||||
const formatter = {
|
||||
proc: spawn(args[0], args.slice(1)),
|
||||
live: true,
|
||||
|
@ -409,7 +423,7 @@ export class Session {
|
|||
};
|
||||
|
||||
ensure = async (cmd) => {
|
||||
const code = await this.run(this.privilegedSpawn(bash(cmd)), {
|
||||
const code = await this.run(this.privilegedExec(bash(cmd)), {
|
||||
check: false,
|
||||
});
|
||||
this.send({ event: "ensured", code });
|
||||
|
@ -422,11 +436,15 @@ export class Session {
|
|||
}
|
||||
this.log(`Tearing down session`);
|
||||
this.tearingDown = true;
|
||||
allSessions.delete(this);
|
||||
if (this.uidInfo) {
|
||||
await this.run(this.privilegedTeardown());
|
||||
await this.returnUser();
|
||||
if (this.container) {
|
||||
// SIGTERM should be sufficient as the command running in the
|
||||
// foreground is just 'tail -f /dev/null' which won't try to
|
||||
// block signals. Killing the foreground process (i.e. pid1)
|
||||
// should cause the Docker runtime to bring everything else
|
||||
// down in flames.
|
||||
this.container.proc.kill();
|
||||
}
|
||||
allSessions.delete(this);
|
||||
this.ws.terminate();
|
||||
} catch (err) {
|
||||
this.log(`Error during teardown`);
|
||||
|
|
|
@ -3,9 +3,9 @@ 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";
|
||||
import { getUUID } from "./util.js";
|
||||
|
||||
import {
|
||||
privilegedSetup,
|
||||
privilegedSpawn,
|
||||
|
@ -29,9 +29,8 @@ async function main() {
|
|||
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 }, [
|
||||
await run(privilegedSetup({ uuid }), log);
|
||||
const args = privilegedSpawn({ uuid }, [
|
||||
"bash",
|
||||
"-c",
|
||||
`exec env L='${lang}' bash --rcfile <(cat <<< ${quote([sandboxScript])})`,
|
||||
|
@ -43,7 +42,7 @@ async function main() {
|
|||
proc.on("error", reject);
|
||||
proc.on("close", resolve);
|
||||
});
|
||||
await run(privilegedTeardown({ uid, uuid }), log);
|
||||
await run(privilegedTeardown({ uuid }), log);
|
||||
await returnUser();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ import _ from "lodash";
|
|||
import pQueue from "p-queue";
|
||||
const PQueue = pQueue.default;
|
||||
import stripAnsi from "strip-ansi";
|
||||
import { v4 as getUUID } from "uuid";
|
||||
|
||||
import * as api from "./api.js";
|
||||
import { langsPromise } from "./langs.js";
|
||||
import { getUUID } from "./util.js";
|
||||
|
||||
let langs = {};
|
||||
|
||||
|
|
115
backend/users.js
115
backend/users.js
|
@ -1,115 +0,0 @@
|
|||
import { spawn } from "child_process";
|
||||
import { promises as fs } from "fs";
|
||||
import os from "os";
|
||||
|
||||
import AsyncLock from "async-lock";
|
||||
import _ from "lodash";
|
||||
import parsePasswd from "parse-passwd";
|
||||
|
||||
import { asBool, privilegedUseradd, run, uuidRegexp } from "./util.js";
|
||||
|
||||
// Keep in sync with system/src/riju-system-privileged.c
|
||||
export const MIN_UID = 2000;
|
||||
export const MAX_UID = 65000;
|
||||
|
||||
function validUID(uid) {
|
||||
return uid >= MIN_UID && uid < MAX_UID;
|
||||
}
|
||||
|
||||
const CUR_UID = os.userInfo().uid;
|
||||
const ASSUME_SINGLE_PROCESS = asBool(
|
||||
process.env.RIJU_ASSUME_SINGLE_PROCESS,
|
||||
false
|
||||
);
|
||||
|
||||
let initialized = false;
|
||||
let nextUserToCreate = null;
|
||||
let locallyBorrowedUsers = new Set();
|
||||
let availableUsers = new Set();
|
||||
let lock = new AsyncLock();
|
||||
|
||||
async function getCreatedUsers() {
|
||||
return new Set(
|
||||
parsePasswd(await fs.readFile("/etc/passwd", "utf-8"))
|
||||
.map(({ uid }) => parseInt(uid))
|
||||
.filter((uid) => !isNaN(uid) && validUID(uid))
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
dirents
|
||||
.filter((name) => name.match(uuidRegexp))
|
||||
.map((name) => fs.stat(`/tmp/riju/${name}`))
|
||||
)
|
||||
)
|
||||
.map(({ uid }) => uid)
|
||||
.filter(validUID)
|
||||
);
|
||||
}
|
||||
|
||||
async function createUser(log) {
|
||||
if (nextUserToCreate >= MAX_UID) {
|
||||
throw new Error("too many users");
|
||||
}
|
||||
const uid = nextUserToCreate;
|
||||
await run(privilegedUseradd(uid), log);
|
||||
nextUserToCreate += 1;
|
||||
return uid;
|
||||
}
|
||||
|
||||
export async function borrowUser(log) {
|
||||
return await lock.acquire("key", async () => {
|
||||
if (!initialized || !ASSUME_SINGLE_PROCESS) {
|
||||
const createdUsers = await getCreatedUsers();
|
||||
const activeUsers = await getActiveUsers();
|
||||
if (createdUsers.size > 0) {
|
||||
nextUserToCreate = _.max([...createdUsers]) + 1;
|
||||
} else {
|
||||
nextUserToCreate = MIN_UID;
|
||||
}
|
||||
// If there are new users created, we want to make them
|
||||
// available (unless they are already active). Similarly, if
|
||||
// there are users that have become inactive, we want to make
|
||||
// them available (unless they are already borrowed locally).
|
||||
for (const user of createdUsers) {
|
||||
if (!activeUsers.has(user) && !locallyBorrowedUsers.has(user)) {
|
||||
availableUsers.add(user);
|
||||
}
|
||||
}
|
||||
// If there are users that have become active, we want to make
|
||||
// them unavailable.
|
||||
for (const user of activeUsers) {
|
||||
availableUsers.delete(user);
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
if (availableUsers.size === 0) {
|
||||
availableUsers.add(await createUser(log));
|
||||
}
|
||||
// https://stackoverflow.com/a/32539929/3538165
|
||||
const user = availableUsers.values().next().value;
|
||||
locallyBorrowedUsers.add(user);
|
||||
availableUsers.delete(user);
|
||||
return {
|
||||
uid: user,
|
||||
returnUser: async () => {
|
||||
await lock.acquire("key", () => {
|
||||
locallyBorrowedUsers.delete(user);
|
||||
availableUsers.add(user);
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
|
@ -3,55 +3,12 @@ import os from "os";
|
|||
import process from "process";
|
||||
|
||||
import { quote } from "shell-quote";
|
||||
|
||||
import { MIN_UID, MAX_UID } from "./users.js";
|
||||
import { v4 as getUUIDOrig } from "uuid";
|
||||
|
||||
export const rijuSystemPrivileged = "system/out/riju-system-privileged";
|
||||
|
||||
const rubyVersion = (() => {
|
||||
try {
|
||||
return spawnSync("ruby", ["-e", "puts RUBY_VERSION"])
|
||||
.stdout.toString()
|
||||
.trim();
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
function getEnv({ uid, uuid }) {
|
||||
const cwd = `/tmp/riju/${uuid}`;
|
||||
const path = [
|
||||
rubyVersion && `${cwd}/.gem/ruby/${rubyVersion}/bin`,
|
||||
`${cwd}/.local/bin`,
|
||||
`${cwd}/node_modules/.bin`,
|
||||
`/usr/local/sbin`,
|
||||
`/usr/local/bin`,
|
||||
`/usr/sbin`,
|
||||
`/usr/bin`,
|
||||
`/bin`,
|
||||
].filter((x) => x);
|
||||
const username =
|
||||
uid >= MIN_UID && uid < MAX_UID ? `riju${uid}` : os.userInfo().username;
|
||||
return {
|
||||
HOME: cwd,
|
||||
HOSTNAME: "riju",
|
||||
LANG: "C.UTF-8",
|
||||
LC_ALL: "C.UTF-8",
|
||||
LOGNAME: username,
|
||||
PATH: path.join(":"),
|
||||
PWD: cwd,
|
||||
SHELL: "/usr/bin/bash",
|
||||
TERM: "xterm-256color",
|
||||
TMPDIR: `${cwd}`,
|
||||
USER: username,
|
||||
USERNAME: username,
|
||||
};
|
||||
}
|
||||
|
||||
function getEnvString(ctx) {
|
||||
return Object.entries(getEnv(ctx))
|
||||
.map(([key, val]) => `${key}=${quote([val])}`)
|
||||
.join(" ");
|
||||
export function getUUID() {
|
||||
return getUUIDOrig().replace(/-/g, "");
|
||||
}
|
||||
|
||||
export async function run(args, log, options) {
|
||||
|
@ -87,30 +44,16 @@ export async function run(args, log, options) {
|
|||
});
|
||||
}
|
||||
|
||||
export function privilegedUseradd(uid) {
|
||||
return [rijuSystemPrivileged, "useradd", `${uid}`];
|
||||
export function privilegedSession({ uuid, lang }) {
|
||||
return [rijuSystemPrivileged, "session", uuid, lang];
|
||||
}
|
||||
|
||||
export function privilegedSetup({ uid, uuid }) {
|
||||
return [rijuSystemPrivileged, "setup", `${uid}`, uuid];
|
||||
export function privilegedWait({ uuid }) {
|
||||
return [rijuSystemPrivileged, "wait", uuid];
|
||||
}
|
||||
|
||||
export function privilegedSpawn(ctx, args) {
|
||||
const { uid, uuid } = ctx;
|
||||
return [
|
||||
rijuSystemPrivileged,
|
||||
"spawn",
|
||||
`${uid}`,
|
||||
uuid,
|
||||
"sh",
|
||||
"-c",
|
||||
`exec env -i ${getEnvString(ctx)} "$@"`,
|
||||
"--",
|
||||
].concat(args);
|
||||
}
|
||||
|
||||
export function privilegedTeardown({ uid, uuid }) {
|
||||
return [rijuSystemPrivileged, "teardown", `${uid}`, uuid];
|
||||
export function privilegedExec({ uuid }, args) {
|
||||
return [rijuSystemPrivileged, "exec", uuid].concat(args);
|
||||
}
|
||||
|
||||
export function bash(cmdline) {
|
||||
|
@ -130,9 +73,6 @@ export const log = {
|
|||
error: console.error,
|
||||
};
|
||||
|
||||
// https://gist.github.com/bugventure/f71337e3927c34132b9a
|
||||
export const uuidRegexp = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
|
||||
|
||||
export function asBool(value, def) {
|
||||
if (def === undefined) {
|
||||
throw new Error("asBool needs an explicit default value");
|
||||
|
|
|
@ -22,17 +22,10 @@ COPY lib ./lib/
|
|||
COPY backend ./backend/
|
||||
COPY langs ./langs/
|
||||
|
||||
FROM ubuntu:rolling
|
||||
FROM riju:runtime
|
||||
|
||||
COPY docker/app/install.bash /tmp/
|
||||
RUN /tmp/install.bash
|
||||
|
||||
COPY docker/shared/my_init /usr/local/sbin/
|
||||
ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--"]
|
||||
|
||||
RUN useradd -p '!' -m -l -s /usr/bin/bash riju
|
||||
|
||||
WORKDIR /src
|
||||
COPY --chown=riju:riju --from=build /src ./
|
||||
RUN chown root:riju system/out/*-privileged && chmod a=,g=rx,u=rwxs system/out/*-privileged
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
apt-get update
|
||||
apt-get dist-upgrade -y
|
||||
|
||||
apt-get install -y curl gnupg lsb-release
|
||||
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
|
||||
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||
|
||||
ubuntu_ver="$(lsb_release -rs)"
|
||||
ubuntu_name="$(lsb_release -cs)"
|
||||
|
||||
node_repo="$(curl -sS https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
|
||||
|
||||
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
|
||||
deb [arch=amd64] https://deb.nodesource.com/${node_repo} ${ubuntu_name} main
|
||||
deb [arch=amd64] https://dl.yarnpkg.com/debian/ stable main
|
||||
EOF
|
||||
|
||||
apt-get update
|
||||
apt-get install -y make nodejs yarn
|
||||
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
rm "$0"
|
|
@ -0,0 +1,9 @@
|
|||
FROM ubuntu:rolling
|
||||
|
||||
COPY docker/base/install.bash /tmp/
|
||||
RUN /tmp/install.bash
|
||||
|
||||
WORKDIR /src
|
||||
COPY docker/shared/my_init /usr/local/sbin/
|
||||
ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--"]
|
||||
CMD ["bash"]
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
latest_release() {
|
||||
curl -sSL "https://api.github.com/repos/$1/releases/latest" | jq -r .tag_name
|
||||
}
|
||||
|
||||
mkdir /tmp/riju-work
|
||||
pushd /tmp/riju-work
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
dpkg --add-architecture i386
|
||||
|
||||
apt-get update
|
||||
(yes || true) | unminimize
|
||||
|
||||
apt-get install -y curl gnupg lsb-release wget
|
||||
|
||||
# Ceylon
|
||||
wget https://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1.crt.pem -O /usr/local/share/ca-certificates/DigiCertTLSRSASHA2562020CA1.crt
|
||||
|
||||
# D
|
||||
wget https://letsencrypt.org/certs/lets-encrypt-r3.pem -O /usr/local/share/ca-certificates/lets-encrypt-r3.crt
|
||||
|
||||
update-ca-certificates
|
||||
|
||||
ubuntu_ver="$(lsb_release -rs)"
|
||||
ubuntu_name="$(lsb_release -cs)"
|
||||
|
||||
cran_repo="$(curl -fsSL https://cran.r-project.org/bin/linux/ubuntu/ | grep -Eo 'cran[0-9]+' | head -n1)"
|
||||
node_repo="$(curl -fsSL https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
|
||||
|
||||
# .NET
|
||||
wget "https://packages.microsoft.com/config/ubuntu/${ubuntu_ver}/packages-microsoft-prod.deb"
|
||||
apt-get install ./packages-microsoft-prod.deb
|
||||
|
||||
# Ceylon
|
||||
curl -fsSL https://downloads.ceylon-lang.org/apt/ceylon-debian-repo.gpg.key | apt-key add -
|
||||
|
||||
# Crystal
|
||||
curl -fsSL https://keybase.io/crystal/pgp_keys.asc | apt-key add -
|
||||
|
||||
# Dart
|
||||
curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
|
||||
# Hack
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B4112585D386EB94
|
||||
|
||||
# MongoDB
|
||||
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
|
||||
|
||||
# Node.js
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
|
||||
|
||||
# R
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
|
||||
|
||||
# Yarn
|
||||
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||
|
||||
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
|
||||
# Ceylon
|
||||
deb [arch=amd64] https://downloads.ceylon-lang.org/apt/ unstable main
|
||||
|
||||
# Crystal
|
||||
deb [arch=amd64] https://dist.crystal-lang.org/apt crystal main
|
||||
|
||||
# Dart
|
||||
deb [arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main
|
||||
|
||||
# Hack
|
||||
deb [arch=amd64] https://dl.hhvm.com/ubuntu ${ubuntu_name} main
|
||||
|
||||
# MongoDB
|
||||
deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse
|
||||
|
||||
# Node.js
|
||||
deb [arch=amd64] https://deb.nodesource.com/${node_repo} ${ubuntu_name} main
|
||||
|
||||
# R
|
||||
deb [arch=amd64] https://cloud.r-project.org/bin/linux/ubuntu ${ubuntu_name}-${cran_repo}/
|
||||
|
||||
# Yarn
|
||||
deb [arch=amd64] https://dl.yarnpkg.com/debian/ stable main
|
||||
EOF
|
||||
|
||||
# Work around brutal packaging error courtesy of Microsoft.
|
||||
# Unfortunately, the Microsoft repo includes a duplicate version of
|
||||
# the libodbc1 package whose version is not in sync with the one
|
||||
# shipped by the corresponding release of Ubuntu. If this one happens
|
||||
# to be newer, then it can cause horrifyingly difficult to diagnose
|
||||
# errors later on because there's a conflict between the
|
||||
# default-available versions of libodbc1 and libodbc1:i386, which has
|
||||
# in the past surfaced as an inability to install dependencies for
|
||||
# Erlang. Thanks Microsoft. Please don't. Anyway, solution is to pin
|
||||
# this repository at a lower priority than the Ubuntu standard
|
||||
# packages, so the correct version of libodbc1 gets installed by
|
||||
# default.
|
||||
tee -a /etc/apt/preferences.d/riju >/dev/null <<EOF
|
||||
Package: *
|
||||
Pin: origin packages.microsoft.com
|
||||
Pin-Priority: 1
|
||||
EOF
|
||||
|
||||
apt-get update
|
||||
apt-get install -y dctrl-tools
|
||||
|
||||
libicu="$(grep-aptavail -wF Package 'libicu[0-9]+' -s Package -n | head -n1)"
|
||||
|
||||
packages="
|
||||
|
||||
# compilation tools
|
||||
clang
|
||||
g++
|
||||
gcc
|
||||
make
|
||||
|
||||
# base languages
|
||||
nodejs
|
||||
ocaml
|
||||
perl
|
||||
python3
|
||||
ruby
|
||||
|
||||
# packaging tools
|
||||
apt-file
|
||||
dctrl-tools
|
||||
|
||||
# basic utilities
|
||||
bind9-dnsutils
|
||||
less
|
||||
git
|
||||
htop
|
||||
jq
|
||||
make
|
||||
man
|
||||
moreutils
|
||||
psmisc
|
||||
ripgrep
|
||||
strace
|
||||
sudo
|
||||
tmux
|
||||
tree
|
||||
vim
|
||||
|
||||
# shared dependencies
|
||||
${libicu}
|
||||
|
||||
"
|
||||
|
||||
apt-get install -y $(sed 's/#.*//' <<< "${packages}")
|
||||
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
tee /etc/sudoers.d/90-riju >/dev/null <<"EOF"
|
||||
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
popd
|
||||
rm -rf /tmp/riju-work
|
||||
|
||||
rm "$0"
|
|
@ -1,4 +1,4 @@
|
|||
FROM riju:runtime
|
||||
FROM riju:base
|
||||
|
||||
ARG LANG
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
set -euxo pipefail
|
||||
|
||||
# See install.bash for the runtime image for much of the same, but
|
||||
# with more comments.
|
||||
# See install.bash for the base image for much of the same, but with
|
||||
# more comments.
|
||||
|
||||
mkdir /tmp/riju-work
|
||||
pushd /tmp/riju-work
|
||||
|
|
|
@ -3,9 +3,10 @@ FROM ubuntu:rolling
|
|||
COPY docker/runtime/install.bash /tmp/
|
||||
RUN /tmp/install.bash
|
||||
|
||||
WORKDIR /src
|
||||
COPY docker/shared/my_init docker/runtime/pid1.bash /usr/local/sbin/
|
||||
ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--", "/usr/local/sbin/pid1.bash"]
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["bash"]
|
||||
EXPOSE 6119
|
||||
EXPOSE 6120
|
||||
|
|
|
@ -11,120 +11,32 @@ pushd /tmp/riju-work
|
|||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
dpkg --add-architecture i386
|
||||
|
||||
apt-get update
|
||||
(yes || true) | unminimize
|
||||
|
||||
apt-get install -y curl gnupg lsb-release wget
|
||||
|
||||
# Ceylon
|
||||
wget https://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1.crt.pem -O /usr/local/share/ca-certificates/DigiCertTLSRSASHA2562020CA1.crt
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
|
||||
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||
|
||||
# D
|
||||
wget https://letsencrypt.org/certs/lets-encrypt-r3.pem -O /usr/local/share/ca-certificates/lets-encrypt-r3.crt
|
||||
|
||||
update-ca-certificates
|
||||
|
||||
ubuntu_ver="$(lsb_release -rs)"
|
||||
ubuntu_name="$(lsb_release -cs)"
|
||||
|
||||
cran_repo="$(curl -fsSL https://cran.r-project.org/bin/linux/ubuntu/ | grep -Eo 'cran[0-9]+' | head -n1)"
|
||||
node_repo="$(curl -fsSL https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
|
||||
|
||||
# .NET
|
||||
wget "https://packages.microsoft.com/config/ubuntu/${ubuntu_ver}/packages-microsoft-prod.deb"
|
||||
apt-get install ./packages-microsoft-prod.deb
|
||||
|
||||
# Ceylon
|
||||
curl -fsSL https://downloads.ceylon-lang.org/apt/ceylon-debian-repo.gpg.key | apt-key add -
|
||||
|
||||
# Crystal
|
||||
curl -fsSL https://keybase.io/crystal/pgp_keys.asc | apt-key add -
|
||||
|
||||
# Dart
|
||||
curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
|
||||
# Hack
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B4112585D386EB94
|
||||
|
||||
# MongoDB
|
||||
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
|
||||
|
||||
# Node.js
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
|
||||
|
||||
# R
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
|
||||
|
||||
# Yarn
|
||||
curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||
node_repo="$(curl -sS https://deb.nodesource.com/setup_current.x | grep NODEREPO= | grep -Eo 'node_[0-9]+\.x' | head -n1)"
|
||||
|
||||
tee -a /etc/apt/sources.list.d/custom.list >/dev/null <<EOF
|
||||
# Ceylon
|
||||
deb [arch=amd64] https://downloads.ceylon-lang.org/apt/ unstable main
|
||||
|
||||
# Crystal
|
||||
deb [arch=amd64] https://dist.crystal-lang.org/apt crystal main
|
||||
|
||||
# Dart
|
||||
deb [arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main
|
||||
|
||||
# Hack
|
||||
deb [arch=amd64] https://dl.hhvm.com/ubuntu ${ubuntu_name} main
|
||||
|
||||
# MongoDB
|
||||
deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse
|
||||
|
||||
# Node.js
|
||||
deb [arch=amd64] https://deb.nodesource.com/${node_repo} ${ubuntu_name} main
|
||||
|
||||
# R
|
||||
deb [arch=amd64] https://cloud.r-project.org/bin/linux/ubuntu ${ubuntu_name}-${cran_repo}/
|
||||
|
||||
# Yarn
|
||||
deb [arch=amd64] https://dl.yarnpkg.com/debian/ stable main
|
||||
deb [arch=amd64] https://download.docker.com/linux/ubuntu ${ubuntu_name} stable
|
||||
EOF
|
||||
|
||||
# Work around brutal packaging error courtesy of Microsoft.
|
||||
# Unfortunately, the Microsoft repo includes a duplicate version of
|
||||
# the libodbc1 package whose version is not in sync with the one
|
||||
# shipped by the corresponding release of Ubuntu. If this one happens
|
||||
# to be newer, then it can cause horrifyingly difficult to diagnose
|
||||
# errors later on because there's a conflict between the
|
||||
# default-available versions of libodbc1 and libodbc1:i386, which has
|
||||
# in the past surfaced as an inability to install dependencies for
|
||||
# Erlang. Thanks Microsoft. Please don't. Anyway, solution is to pin
|
||||
# this repository at a lower priority than the Ubuntu standard
|
||||
# packages, so the correct version of libodbc1 gets installed by
|
||||
# default.
|
||||
tee -a /etc/apt/preferences.d/riju >/dev/null <<EOF
|
||||
Package: *
|
||||
Pin: origin packages.microsoft.com
|
||||
Pin-Priority: 1
|
||||
EOF
|
||||
|
||||
apt-get update
|
||||
apt-get install -y dctrl-tools
|
||||
|
||||
libicu="$(grep-aptavail -wF Package 'libicu[0-9]+' -s Package -n | head -n1)"
|
||||
|
||||
packages="
|
||||
|
||||
# compilation tools
|
||||
clang
|
||||
g++
|
||||
gcc
|
||||
make
|
||||
|
||||
# base languages
|
||||
nodejs
|
||||
ocaml
|
||||
perl
|
||||
python3
|
||||
ruby
|
||||
|
||||
# project tools
|
||||
clang
|
||||
docker-ce-cli
|
||||
make
|
||||
nodejs
|
||||
yarn
|
||||
|
||||
# packaging tools
|
||||
|
@ -148,11 +60,9 @@ tmux
|
|||
tree
|
||||
vim
|
||||
|
||||
# shared dependencies
|
||||
${libicu}
|
||||
|
||||
"
|
||||
|
||||
apt-get update
|
||||
apt-get install -y $(sed 's/#.*//' <<< "${packages}")
|
||||
|
||||
ver="$(latest_release watchexec/watchexec)"
|
||||
|
@ -166,9 +76,6 @@ tee /etc/sudoers.d/90-riju >/dev/null <<"EOF"
|
|||
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
|
||||
EOF
|
||||
|
||||
mkdir -p /opt/riju/langs
|
||||
touch /opt/riju/langs/.keep
|
||||
|
||||
popd
|
||||
rm -rf /tmp/riju-work
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ for src in system/src/*.c; do
|
|||
out="${out/.c}"
|
||||
verbosely clang -Wall -Wextra -Werror -std=c11 "${src}" -o "${out}"
|
||||
if [[ "${out}" == *-privileged ]]; then
|
||||
if getent group riju >/dev/null; then
|
||||
sudo chown root:riju "${out}"
|
||||
fi
|
||||
sudo chmod a=,g=rx,u=rwxs "${out}"
|
||||
verbosely sudo chown root:riju "${out}"
|
||||
verbosely sudo chmod a=,g=rx,u=rwxs "${out}"
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Keep in sync with backend/src/users.ts
|
||||
const int MIN_UID = 2000;
|
||||
const int MAX_UID = 65000;
|
||||
|
||||
int privileged;
|
||||
|
||||
void __attribute__ ((noreturn)) die(char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
|
@ -23,155 +19,136 @@ void __attribute__ ((noreturn)) die(char *msg)
|
|||
void die_with_usage()
|
||||
{
|
||||
die("usage:\n"
|
||||
" riju-system-privileged useradd UID\n"
|
||||
" riju-system-privileged setup UID UUID\n"
|
||||
" riju-system-privileged spawn UID UUID CMDLINE...\n"
|
||||
" riju-system-privileged teardown UID UUID");
|
||||
}
|
||||
|
||||
int parseUID(char *str)
|
||||
{
|
||||
if (!privileged)
|
||||
return -1;
|
||||
char *endptr;
|
||||
long uid = strtol(str, &endptr, 10);
|
||||
if (!*str || *endptr)
|
||||
die("uid must be an integer");
|
||||
if (uid < MIN_UID || uid >= MAX_UID)
|
||||
die("uid is out of range");
|
||||
return uid;
|
||||
" riju-system-privileged session UUID LANG\n"
|
||||
" riju-system-privileged wait UUID\n"
|
||||
" riju-system-privileged exec UUID CMDLINE...");
|
||||
}
|
||||
|
||||
char *parseUUID(char *uuid)
|
||||
{
|
||||
if (!*uuid)
|
||||
if (strnlen(uuid, 33) != 32)
|
||||
die("illegal uuid");
|
||||
for (char *ptr = uuid; *ptr; ++ptr)
|
||||
if (!((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= '0' && *ptr <= '9') || *ptr == '-'))
|
||||
if (!((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= '0' && *ptr <= '9')))
|
||||
die("illegal uuid");
|
||||
return uuid;
|
||||
}
|
||||
|
||||
void useradd(int uid)
|
||||
{
|
||||
if (!privileged)
|
||||
die("useradd not allowed without root privileges");
|
||||
char *cmdline;
|
||||
if (asprintf(&cmdline, "groupadd -g %1$d riju%1$d", uid) < 0)
|
||||
die("asprintf failed");
|
||||
int status = system(cmdline);
|
||||
if (status != 0)
|
||||
die("groupadd failed");
|
||||
if (asprintf(&cmdline, "useradd -M -N -l -r -u %1$d -g %1$d -p '!' -s /usr/bin/bash riju%1$d", uid) < 0)
|
||||
die("asprintf failed");
|
||||
status = system(cmdline);
|
||||
if (status != 0)
|
||||
die("useradd failed");
|
||||
char *parseLang(char *lang) {
|
||||
size_t len = strnlen(lang, 65);
|
||||
if (len == 0 || len > 64)
|
||||
die("illegal language name");
|
||||
return lang;
|
||||
}
|
||||
|
||||
void spawn(int uid, char *uuid, char **cmdline)
|
||||
void session(char *uuid, char *lang)
|
||||
{
|
||||
char *cwd;
|
||||
if (asprintf(&cwd, "/tmp/riju/%s", uuid) < 0)
|
||||
char *image, *container;
|
||||
if (asprintf(&image, "riju:lang-%s", lang) < 0)
|
||||
die("asprintf failed");
|
||||
if (chdir(cwd) < 0)
|
||||
die("chdir failed");
|
||||
if (privileged) {
|
||||
if (setgid(uid) < 0)
|
||||
die("setgid failed");
|
||||
if (setgroups(0, NULL) < 0)
|
||||
die("setgroups failed");
|
||||
if (setuid(uid) < 0)
|
||||
die("setuid failed");
|
||||
}
|
||||
umask(077);
|
||||
execvp(cmdline[0], cmdline);
|
||||
if (asprintf(&container, "riju-session-%s", uuid) < 0)
|
||||
die("asprintf failed");
|
||||
char *argv[] = {
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"-e", "HOME=/home/riju",
|
||||
"-e", "HOSTNAME=riju",
|
||||
"-e", "LANG=C.UTF-8",
|
||||
"-e", "LC_ALL=C.UTF-8",
|
||||
"-e", "LOGNAME=riju",
|
||||
"-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin",
|
||||
"-e", "PWD=/home/riju/src",
|
||||
"-e", "SHELL=/usr/bin/bash",
|
||||
"-e", "TERM=xterm-256color",
|
||||
"-e", "TMPDIR=/tmp",
|
||||
"-e", "USER=riju",
|
||||
"-e", "USERNAME=riju",
|
||||
"--hostname", "riju",
|
||||
"--name", container,
|
||||
image, "tail", "-f", "/dev/null", NULL,
|
||||
};
|
||||
execvp(argv[0], argv);
|
||||
die("execvp failed");
|
||||
}
|
||||
|
||||
void setup(int uid, char *uuid)
|
||||
void wait_alarm(int signum)
|
||||
{
|
||||
char *cmdline;
|
||||
if (asprintf(&cmdline, privileged
|
||||
? "install -d -o riju%1$d -g riju%1$d -m 700 /tmp/riju/%2$s"
|
||||
: "install -d -m 700 /tmp/riju/%2$s", uid, uuid) < 0)
|
||||
die("asprintf failed");
|
||||
int status = system(cmdline);
|
||||
if (status != 0)
|
||||
die("install failed");
|
||||
(void)signum;
|
||||
die("container did not come up within 1 second");
|
||||
}
|
||||
|
||||
void teardown(int uid, char *uuid)
|
||||
void wait(char *uuid)
|
||||
{
|
||||
char *cmdline;
|
||||
int status;
|
||||
char *users;
|
||||
if (uid >= MIN_UID && uid < MAX_UID) {
|
||||
if (asprintf(&users, "%d", uid) < 0)
|
||||
die("asprintf failed");
|
||||
} else {
|
||||
cmdline = "getent passwd | grep -Eo '^riju[0-9]{4}' | paste -s -d, - | tr -d '\n'";
|
||||
FILE *fp = popen(cmdline, "r");
|
||||
if (fp == NULL)
|
||||
die("popen failed");
|
||||
static char buf[(MAX_UID - MIN_UID) * 9];
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL) {
|
||||
if (feof(fp))
|
||||
users = NULL;
|
||||
else {
|
||||
die("fgets failed");
|
||||
}
|
||||
} else
|
||||
users = buf;
|
||||
}
|
||||
if (users != NULL) {
|
||||
if (asprintf(&cmdline, "while pkill -9 --uid %1$s; do sleep 0.01; done", users) < 0)
|
||||
die("asprintf failed");
|
||||
status = system(cmdline);
|
||||
if (status != 0 && status != 256)
|
||||
die("pkill failed");
|
||||
}
|
||||
if (asprintf(&cmdline, "rm -rf /tmp/riju/%s", uuid) < 0)
|
||||
if (asprintf(&cmdline, "docker inspect riju-session-%s", uuid) < 0)
|
||||
die("asprintf failed");
|
||||
status = system(cmdline);
|
||||
if (status != 0)
|
||||
die("rm failed");
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000 * 1000 * 10;
|
||||
signal(SIGALRM, wait_alarm);
|
||||
alarm(1);
|
||||
while (1) {
|
||||
FILE *proc = popen(cmdline, "r");
|
||||
if (proc == NULL)
|
||||
die("popen failed");
|
||||
int status = pclose(proc);
|
||||
if (status < 0)
|
||||
die("pclose failed");
|
||||
if (WEXITSTATUS(status) == 0)
|
||||
break;
|
||||
int rv = nanosleep(&ts, NULL);
|
||||
if (rv != 0 && rv != EINTR)
|
||||
die("nanosleep failed");
|
||||
}
|
||||
}
|
||||
|
||||
void exec(char *uuid, int argc, char **cmdline)
|
||||
{
|
||||
char *container;
|
||||
if (asprintf(&container, "riju-session-%s", uuid) < 0)
|
||||
die("asprintf failed");
|
||||
char *argvPrefix[] = {
|
||||
"docker",
|
||||
"exec",
|
||||
"-it",
|
||||
container,
|
||||
};
|
||||
char **argv = malloc(sizeof(argvPrefix) + (argc + 1) * sizeof(char *));
|
||||
if (argv == NULL)
|
||||
die("malloc failed");
|
||||
memcpy(argv, argvPrefix, sizeof(argvPrefix));
|
||||
memcpy((void *)argv + sizeof(argvPrefix), cmdline, argc * sizeof(char *));
|
||||
argv[sizeof(argvPrefix) + argc * sizeof(char *)] = NULL;
|
||||
execvp(argv[0], argv);
|
||||
die("execvp failed");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int code = setuid(0);
|
||||
if (code != 0 && code != -EPERM)
|
||||
if (setuid(0) != 0)
|
||||
die("setuid failed");
|
||||
privileged = code == 0;
|
||||
if (argc < 2)
|
||||
die_with_usage();
|
||||
if (!strcmp(argv[1], "useradd")) {
|
||||
if (!strcmp(argv[1], "session")) {
|
||||
if (argc != 4)
|
||||
die_with_usage();
|
||||
char *uuid = parseUUID(argv[2]);
|
||||
char *lang = parseLang(argv[3]);
|
||||
session(uuid, lang);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "wait")) {
|
||||
if (argc != 3)
|
||||
die_with_usage();
|
||||
useradd(parseUID(argv[2]));
|
||||
char *uuid = parseUUID(argv[2]);
|
||||
wait(uuid);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "spawn")) {
|
||||
if (argc < 5)
|
||||
if (!strcmp(argv[1], "exec")) {
|
||||
if (argc < 4)
|
||||
die_with_usage();
|
||||
spawn(parseUID(argv[2]), parseUUID(argv[3]), &argv[4]);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "setup")) {
|
||||
if (argc != 4)
|
||||
die_with_usage();
|
||||
int uid = parseUID(argv[2]);
|
||||
char *uuid = parseUUID(argv[3]);
|
||||
setup(uid, uuid);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(argv[1], "teardown")) {
|
||||
if (argc != 4)
|
||||
die_with_usage();
|
||||
int uid = strcmp(argv[2], "*") ? parseUID(argv[2]) : -1;
|
||||
char *uuid = strcmp(argv[3], "*") ? parseUUID(argv[3]) : "*";
|
||||
teardown(uid, uuid);
|
||||
exec(parseUUID(argv[2]), argc, &argv[3]);
|
||||
return 0;
|
||||
}
|
||||
die_with_usage();
|
||||
|
|
|
@ -28,10 +28,7 @@ async function main() {
|
|||
const hash = await hashDockerfile(
|
||||
"lang",
|
||||
{
|
||||
"riju:runtime": await getLocalImageLabel(
|
||||
"riju:runtime",
|
||||
"riju.image-hash"
|
||||
),
|
||||
"riju:base": await getLocalImageLabel("riju:base", "riju.image-hash"),
|
||||
},
|
||||
{
|
||||
salt: {
|
||||
|
@ -52,7 +49,7 @@ async function main() {
|
|||
try {
|
||||
if (debug) {
|
||||
await runCommand(
|
||||
`docker run -it --rm -e LANG=${lang} -w /tmp/riju-work --network host riju:runtime`
|
||||
`docker run -it --rm -e LANG=${lang} -w /tmp/riju-work --network host base:runtime`
|
||||
);
|
||||
} else {
|
||||
await runCommand(
|
||||
|
|
|
@ -12,7 +12,7 @@ import _ from "lodash";
|
|||
import { getLocalImageDigest, getLocalImageLabel } from "./docker-util.js";
|
||||
import { runCommand } from "./util.js";
|
||||
|
||||
// Given a string like "runtime" that identifies the relevant
|
||||
// Given a string like "base" that identifies the relevant
|
||||
// Dockerfile, read it from disk and parse it into a list of commands.
|
||||
async function parseDockerfile(name) {
|
||||
const contents = await fs.readFile(`docker/${name}/Dockerfile`, "utf-8");
|
||||
|
|
|
@ -149,9 +149,7 @@ async function planDebianPackages(opts) {
|
|||
}
|
||||
clauses.push(`make installs L=${lang}`);
|
||||
clauses.push("make test");
|
||||
await runCommand(
|
||||
`make shell I=runtime CMD="${clauses.join(" && ")}"`
|
||||
);
|
||||
await runCommand(`make shell I=base CMD="${clauses.join(" && ")}"`);
|
||||
}
|
||||
await runCommand(`make upload L=${lang} T=${type}`);
|
||||
},
|
||||
|
@ -186,12 +184,12 @@ async function computePlan() {
|
|||
"ubuntu:rolling": await getLocalImageDigest("ubuntu:rolling"),
|
||||
};
|
||||
const packaging = await planDockerImage("packaging", dependentHashes);
|
||||
const runtime = await planDockerImage("runtime", dependentHashes);
|
||||
const base = await planDockerImage("base", dependentHashes);
|
||||
const packages = await planDebianPackages({
|
||||
deps: [packaging.id, runtime.id],
|
||||
deps: [packaging.id, base.id],
|
||||
});
|
||||
const composite = await planDockerImage("composite", dependentHashes, {
|
||||
deps: [runtime.id, ...packages.map(({ id }) => id)],
|
||||
deps: [base.id, ...packages.map(({ id }) => id)],
|
||||
hashOpts: {
|
||||
salt: {
|
||||
packageHashes: packages.map(({ desired }) => desired).sort(),
|
||||
|
@ -202,7 +200,7 @@ async function computePlan() {
|
|||
const app = await planDockerImage("app", dependentHashes, {
|
||||
deps: [composite.id, compile.id],
|
||||
});
|
||||
return [packaging, runtime, ...packages, composite, compile, app];
|
||||
return [packaging, base, ...packages, composite, compile, app];
|
||||
}
|
||||
|
||||
function printTable(data, headers) {
|
||||
|
|
Loading…
Reference in New Issue