smc-hub
Version:
CoCalc: Backend webserver component
281 lines (280 loc) • 13.8 kB
JavaScript
"use strict";
/*
This is meant to run on a multi-user system, but where the hub
runs as a single user and all projects also run as that same
user, but with there own HOME directories. There is thus no
security or isolation at all between projects. There is still
a notion of multiple cocalc projects and cocalc users.
This is useful for:
- development of cocalc from inside of a CoCalc project
- non-collaborative use of cocalc on your own
laptop, e.g., when you're on an airplane.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var process_1 = require("process");
var util_1 = require("./util");
var base_1 = require("./base");
var logger_1 = __importDefault(require("smc-hub/logger"));
var winston = logger_1.default("project-control:single-user");
// Usually should fully start in about 5 seconds, but we give it 20s.
var MAX_START_TIME_MS = 20000;
var MAX_STOP_TIME_MS = 10000;
var Project = /** @class */ (function (_super) {
__extends(Project, _super);
function Project(project_id) {
var _this = _super.call(this, project_id) || this;
_this.HOME = util_1.homePath(_this.project_id);
return _this;
}
Project.prototype.state = function () {
return __awaiter(this, void 0, void 0, function () {
var state;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.stateChanging != null) {
return [2 /*return*/, this.stateChanging];
}
return [4 /*yield*/, util_1.getState(this.HOME)];
case 1:
state = _a.sent();
this.saveStateToDatabase(state);
return [2 /*return*/, state];
}
});
});
};
Project.prototype.status = function () {
return __awaiter(this, void 0, void 0, function () {
var status;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, util_1.getStatus(this.HOME)];
case 1:
status = _a.sent();
// TODO: don't include secret token in log message.
winston.debug("got status of " + this.project_id + " = " + JSON.stringify(status));
this.saveStatusToDatabase(status);
return [2 /*return*/, status];
}
});
});
};
Project.prototype.start = function () {
return __awaiter(this, void 0, void 0, function () {
var HOME, env;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
winston.info("start " + this.project_id);
if (this.stateChanging != null)
return [2 /*return*/];
HOME = this.HOME;
return [4 /*yield*/, util_1.isProjectRunning(HOME)];
case 1:
if (!_a.sent()) return [3 /*break*/, 3];
winston.debug("start -- already running");
return [4 /*yield*/, this.saveStateToDatabase({ state: "running" })];
case 2:
_a.sent();
return [2 /*return*/];
case 3:
_a.trys.push([3, , 12, 14]);
this.stateChanging = { state: "starting" };
return [4 /*yield*/, this.saveStateToDatabase(this.stateChanging)];
case 4:
_a.sent();
return [4 /*yield*/, this.siteLicenseHook()];
case 5:
_a.sent();
return [4 /*yield*/, util_1.mkdir(HOME, { recursive: true })];
case 6:
_a.sent();
return [4 /*yield*/, util_1.ensureConfFilesExists(HOME)];
case 7:
_a.sent();
return [4 /*yield*/, util_1.getEnvironment(this.project_id)];
case 8:
env = _a.sent();
winston.debug("start " + this.project_id + ": env = " + JSON.stringify(env));
// Setup files
return [4 /*yield*/, util_1.setupDataPath(HOME)];
case 9:
// Setup files
_a.sent();
// Fork and launch project server
return [4 /*yield*/, util_1.launchProjectDaemon(env)];
case 10:
// Fork and launch project server
_a.sent();
return [4 /*yield*/, this.wait({
until: function () { return __awaiter(_this, void 0, void 0, function () {
var status;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, util_1.isProjectRunning(this.HOME)];
case 1:
if (!(_a.sent())) {
return [2 /*return*/, false];
}
return [4 /*yield*/, this.status()];
case 2:
status = _a.sent();
return [2 /*return*/, !!status.secret_token && !!status["hub-server.port"]];
}
});
}); },
maxTime: MAX_START_TIME_MS,
})];
case 11:
_a.sent();
return [3 /*break*/, 14];
case 12:
this.stateChanging = undefined;
// ensure state valid in database
return [4 /*yield*/, this.state()];
case 13:
// ensure state valid in database
_a.sent();
return [7 /*endfinally*/];
case 14: return [2 /*return*/];
}
});
});
};
Project.prototype.stop = function () {
return __awaiter(this, void 0, void 0, function () {
var pid, _err_1;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.stateChanging != null)
return [2 /*return*/];
winston.info("stop ", this.project_id);
return [4 /*yield*/, util_1.isProjectRunning(this.HOME)];
case 1:
if (!!(_a.sent())) return [3 /*break*/, 3];
return [4 /*yield*/, this.saveStateToDatabase({ state: "opened" })];
case 2:
_a.sent();
return [2 /*return*/];
case 3:
_a.trys.push([3, , 10, 12]);
this.stateChanging = { state: "stopping" };
return [4 /*yield*/, this.saveStateToDatabase(this.stateChanging)];
case 4:
_a.sent();
_a.label = 5;
case 5:
_a.trys.push([5, 7, , 8]);
return [4 /*yield*/, util_1.getProjectPID(this.HOME)];
case 6:
pid = _a.sent();
process_1.kill(-pid);
return [3 /*break*/, 8];
case 7:
_err_1 = _a.sent();
return [3 /*break*/, 8];
case 8: return [4 /*yield*/, this.wait({
until: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, util_1.isProjectRunning(this.HOME)];
case 1: return [2 /*return*/, !(_a.sent())];
}
}); }); },
maxTime: MAX_STOP_TIME_MS,
})];
case 9:
_a.sent();
return [3 /*break*/, 12];
case 10:
this.stateChanging = undefined;
// ensure state valid.
return [4 /*yield*/, this.state()];
case 11:
// ensure state valid.
_a.sent();
return [7 /*endfinally*/];
case 12: return [2 /*return*/];
}
});
});
};
Project.prototype.copyPath = function (opts) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
winston.debug("copyPath ", this.project_id, opts);
return [4 /*yield*/, util_1.copyPath(opts, this.project_id)];
case 1:
_a.sent();
return [2 /*return*/, ""];
}
});
});
};
return Project;
}(base_1.BaseProject));
function get(project_id) {
var _a;
return (_a = base_1.getProject(project_id)) !== null && _a !== void 0 ? _a : new Project(project_id);
}
exports.default = get;
//# sourceMappingURL=single-user.js.map