@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
128 lines (124 loc) • 5.08 kB
JavaScript
"use strict";
/*
multi-user: a multi-user Linux system where the hub runs as root,
so can create and delete user accounts, etc.
There is some security and isolation between projects, coming from
different operating system users.
This is mainly used for cocalc-docker, which is a deployment of
CoCalc running in a single docker container, with one hub running
as root.
This **executes some basic shell commands** (e.g., useradd, rsync)
to start and stop the project, copy files between projects, etc.
This code is very similar to single-user.ts, except with some
small modifications due to having to create and delete Linux users.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("./util");
const base_1 = require("./base");
const logger_1 = __importDefault(require("@cocalc/backend/logger"));
const misc_1 = require("@cocalc/backend/misc");
const winston = (0, logger_1.default)("project-control:multi-user");
const MAX_START_TIME_MS = 30000;
const MAX_STOP_TIME_MS = 20000;
class Project extends base_1.BaseProject {
constructor(project_id) {
super(project_id);
this.HOME = (0, util_1.homePath)(this.project_id);
this.uid = (0, misc_1.getUid)(this.project_id);
}
async state() {
if (this.stateChanging != null) {
return this.stateChanging;
}
const state = await (0, util_1.getState)(this.HOME);
winston.debug(`got state of ${this.project_id} = ${JSON.stringify(state)}`);
this.saveStateToDatabase(state);
return state;
}
async status() {
const status = await (0, util_1.getStatus)(this.HOME);
// TODO: don't include secret token in log message.
winston.debug(`got status of ${this.project_id} = ${JSON.stringify(status)}`);
this.saveStatusToDatabase(status);
return status;
}
async start() {
if (this.stateChanging != null)
return;
winston.info(`start ${this.project_id}`);
// Home directory
const HOME = this.HOME;
if (await (0, util_1.isProjectRunning)(HOME)) {
winston.debug("start -- already running");
await this.saveStateToDatabase({ state: "running" });
return;
}
try {
this.stateChanging = { state: "starting" };
await this.saveStateToDatabase(this.stateChanging);
await this.siteLicenseHook();
await (0, util_1.mkdir)(HOME, { recursive: true });
await (0, util_1.createUser)(this.project_id);
await (0, util_1.chown)(HOME, this.uid);
await (0, util_1.ensureConfFilesExists)(HOME, this.uid);
// this.get('env') = extra env vars for project (from synctable):
const env = await (0, util_1.getEnvironment)(this.project_id);
winston.debug(`start ${this.project_id}: env = ${JSON.stringify(env)}`);
// Setup files
await (0, util_1.setupDataPath)(HOME, this.uid);
// Fork and launch project server daemon
await (0, util_1.launchProjectDaemon)(env, this.uid);
await this.wait({
until: async () => {
if (!(await (0, util_1.isProjectRunning)(this.HOME))) {
return false;
}
const status = await this.status();
return !!status.secret_token && !!status["hub-server.port"];
},
maxTime: MAX_START_TIME_MS,
});
}
finally {
this.stateChanging = undefined;
// ensure state valid
await this.state();
}
}
async stop() {
if (this.stateChanging != null)
return;
winston.info("stop ", this.project_id);
if (!(await (0, util_1.isProjectRunning)(this.HOME))) {
await this.saveStateToDatabase({ state: "opened" });
return;
}
try {
this.stateChanging = { state: "stopping" };
await this.saveStateToDatabase(this.stateChanging);
await (0, util_1.deleteUser)(this.project_id);
await this.wait({
until: async () => !(await (0, util_1.isProjectRunning)(this.HOME)),
maxTime: MAX_STOP_TIME_MS,
});
}
finally {
this.stateChanging = undefined;
// ensure state valid in database
await this.state();
}
}
async copyPath(opts) {
winston.debug("copyPath ", this.project_id, opts);
await (0, util_1.copyPath)(opts, this.project_id, opts.target_project_id ? (0, misc_1.getUid)(opts.target_project_id) : undefined);
return "";
}
}
function get(project_id) {
return (0, base_1.getProject)(project_id) ?? new Project(project_id);
}
exports.default = get;
//# sourceMappingURL=multi-user.js.map