mirror of
https://github.com/caprover/caprover
synced 2026-08-25 00:46:38 +00:00
remove frontend and built backend from source
This commit is contained in:
+4
-43
@@ -1,53 +1,14 @@
|
||||
temp_upload
|
||||
currentdirectory
|
||||
|
||||
# Node modules
|
||||
/temp_upload
|
||||
/currentdirectory
|
||||
/node_modules
|
||||
/built
|
||||
/dist-frontend
|
||||
|
||||
# JetBrains
|
||||
.idea
|
||||
|
||||
/temp_upload
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://docs.npmjs.com/cli/shrinkwrap#caveats
|
||||
node_modules
|
||||
|
||||
.idea
|
||||
|
||||
# Debug log from npm
|
||||
npm-debug.log
|
||||
|
||||
|
||||
# -------------------
|
||||
|
||||
.DS_Store
|
||||
|
||||
node_modules
|
||||
|
||||
lib/core/metadata.js
|
||||
lib/core/MetadataBlog.js
|
||||
@@ -1,43 +0,0 @@
|
||||
"use strict";
|
||||
const CaptainError_1 = require("./CaptainError");
|
||||
const BaseApi = require("./BaseApi");
|
||||
const Logger = require("../utils/Logger");
|
||||
class ApiStatusCodes {
|
||||
static createError(code, message) {
|
||||
return new CaptainError_1.CaptainError(code, message || 'NONE');
|
||||
}
|
||||
static createCatcher(res) {
|
||||
return function (error) {
|
||||
if (!error || error.errorStatus !== 404) {
|
||||
Logger.e(error);
|
||||
}
|
||||
if (error && error.captainErrorType) {
|
||||
res.send(new BaseApi(error.captainErrorType, error.apiMessage));
|
||||
return;
|
||||
}
|
||||
if (error && error.errorStatus) {
|
||||
res.sendStatus(Number(error.errorStatus));
|
||||
return;
|
||||
}
|
||||
res.sendStatus(500);
|
||||
};
|
||||
}
|
||||
}
|
||||
ApiStatusCodes.STATUS_OK_DEPLOY_STARTED = 101;
|
||||
ApiStatusCodes.STATUS_ERROR_GENERIC = 1000;
|
||||
ApiStatusCodes.STATUS_OK = 100;
|
||||
ApiStatusCodes.STATUS_ERROR_CAPTAIN_NOT_INITIALIZED = 1001;
|
||||
ApiStatusCodes.STATUS_ERROR_USER_NOT_INITIALIZED = 1101;
|
||||
ApiStatusCodes.STATUS_ERROR_NOT_AUTHORIZED = 1102;
|
||||
ApiStatusCodes.STATUS_ERROR_ALREADY_EXIST = 1103;
|
||||
ApiStatusCodes.STATUS_ERROR_BAD_NAME = 1104;
|
||||
ApiStatusCodes.STATUS_WRONG_PASSWORD = 1105;
|
||||
ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID = 1106;
|
||||
ApiStatusCodes.VERIFICATION_FAILED = 1107;
|
||||
ApiStatusCodes.ILLEGAL_OPERATION = 1108;
|
||||
ApiStatusCodes.BUILD_ERROR = 1109;
|
||||
ApiStatusCodes.ILLEGAL_PARAMETER = 1110;
|
||||
ApiStatusCodes.NOT_FOUND = 1111;
|
||||
ApiStatusCodes.AUTHENTICATION_FAILED = 1112;
|
||||
module.exports = ApiStatusCodes;
|
||||
//# sourceMappingURL=ApiStatusCodes.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"ApiStatusCodes.js","sourceRoot":"","sources":["../../src/api/ApiStatusCodes.ts"],"names":[],"mappings":";AAAA,iDAA6C;AAE7C,qCAAqC;AACrC,0CAA0C;AAE1C,MAAM,cAAc;IAChB,MAAM,CAAC,WAAW,CAAC,IAAY,EAAE,OAAe;QAC5C,OAAO,IAAI,2BAAY,CAAC,IAAI,EAAE,OAAO,IAAI,MAAM,CAAC,CAAA;IACpD,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,GAAa;QAC9B,OAAO,UAAS,KAAyB;YACrC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,WAAW,KAAK,GAAG,EAAE;gBACrC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;aAClB;YAED,IAAI,KAAK,IAAI,KAAK,CAAC,gBAAgB,EAAE;gBACjC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;gBAC/D,OAAM;aACT;YAED,IAAI,KAAK,IAAI,KAAK,CAAC,WAAW,EAAE;gBAC5B,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAA;gBACzC,OAAM;aACT;YAED,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACvB,CAAC,CAAA;IACL,CAAC;;AAEe,uCAAwB,GAAG,GAAG,CAAA;AAC9B,mCAAoB,GAAG,IAAI,CAAA;AAC3B,wBAAS,GAAG,GAAG,CAAA;AACf,mDAAoC,GAAG,IAAI,CAAA;AAC3C,gDAAiC,GAAG,IAAI,CAAA;AACxC,0CAA2B,GAAG,IAAI,CAAA;AAClC,yCAA0B,GAAG,IAAI,CAAA;AACjC,oCAAqB,GAAG,IAAI,CAAA;AAC5B,oCAAqB,GAAG,IAAI,CAAA;AAC5B,wCAAyB,GAAG,IAAI,CAAA;AAChC,kCAAmB,GAAG,IAAI,CAAA;AAC1B,gCAAiB,GAAG,IAAI,CAAA;AACxB,0BAAW,GAAG,IAAI,CAAA;AAClB,gCAAiB,GAAG,IAAI,CAAA;AACxB,wBAAS,GAAG,IAAI,CAAA;AAChB,oCAAqB,GAAG,IAAI,CAAA;AAGhD,iBAAS,cAAc,CAAA"}
|
||||
@@ -1,10 +0,0 @@
|
||||
"use strict";
|
||||
class BaseApi {
|
||||
constructor(status, description) {
|
||||
this.status = status;
|
||||
this.description = description;
|
||||
//
|
||||
}
|
||||
}
|
||||
module.exports = BaseApi;
|
||||
//# sourceMappingURL=BaseApi.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"BaseApi.js","sourceRoot":"","sources":["../../src/api/BaseApi.ts"],"names":[],"mappings":";AAAA,MAAM,OAAO;IAGT,YAAoB,MAAc,EAAU,WAAmB;QAA3C,WAAM,GAAN,MAAM,CAAQ;QAAU,gBAAW,GAAX,WAAW,CAAQ;QAC3D,EAAE;IACN,CAAC;CACJ;AAED,iBAAS,OAAO,CAAA"}
|
||||
@@ -1,11 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class CaptainError extends Error {
|
||||
constructor(code, msg) {
|
||||
super(msg);
|
||||
this.captainErrorType = code;
|
||||
this.apiMessage = msg;
|
||||
}
|
||||
}
|
||||
exports.CaptainError = CaptainError;
|
||||
//# sourceMappingURL=CaptainError.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CaptainError.js","sourceRoot":"","sources":["../../src/api/CaptainError.ts"],"names":[],"mappings":";;AAAA,MAAa,YAAa,SAAQ,KAAK;IAInC,YAAY,IAAY,EAAE,GAAW;QACjC,KAAK,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAA;QAC5B,IAAI,CAAC,UAAU,GAAG,GAAG,CAAA;IACzB,CAAC;CACJ;AATD,oCASC"}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const path = require("path");
|
||||
const favicon = require("serve-favicon");
|
||||
const logger = require("morgan");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const bodyParser = require("body-parser");
|
||||
const httpProxyImport = require("http-proxy");
|
||||
const CaptainManager = require("./user/system/CaptainManager");
|
||||
const BaseApi = require("./api/BaseApi");
|
||||
const ApiStatusCodes = require("./api/ApiStatusCodes");
|
||||
const Injector = require("./injection/Injector");
|
||||
const Logger = require("./utils/Logger");
|
||||
const CaptainConstants = require("./utils/CaptainConstants");
|
||||
const LoginRouter = require("./routes/LoginRouter");
|
||||
const UserRouter = require("./routes/UserRouter");
|
||||
const InjectionExtractor = require("./injection/InjectionExtractor");
|
||||
// import { NextFunction, Request, Response } from 'express'
|
||||
const httpProxy = httpProxyImport.createProxyServer({});
|
||||
let app = express();
|
||||
app.set('views', path.join(__dirname, '../views'));
|
||||
app.set('view engine', 'ejs');
|
||||
app.use(favicon(path.join(__dirname, '../public', 'favicon.ico')));
|
||||
app.use(logger('dev'));
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: false,
|
||||
}));
|
||||
app.use(cookieParser());
|
||||
if (CaptainConstants.isDebug) {
|
||||
app.use('*', function (req, res, next) {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
res.setHeader('Access-Control-Allow-Headers', CaptainConstants.headerNamespace +
|
||||
',' +
|
||||
CaptainConstants.headerAuth +
|
||||
',Content-Type');
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
app.use('/force-exit', function (req, res, next) {
|
||||
res.send('Okay... I will exit in a second...');
|
||||
setTimeout(function () {
|
||||
process.exit(0);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
app.use(Injector.injectGlobal());
|
||||
app.use(function (req, res, next) {
|
||||
if (InjectionExtractor.extractGlobalsFromInjected(res).forceSsl) {
|
||||
let isRequestSsl = req.secure || req.get('X-Forwarded-Proto') === 'https';
|
||||
if (!isRequestSsl) {
|
||||
let newUrl = 'https://' + req.get('host') + req.originalUrl;
|
||||
res.redirect(302, newUrl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
app.use(express.static(path.join(__dirname, '../dist-frontend')));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use(CaptainConstants.healthCheckEndPoint, function (req, res, next) {
|
||||
res.send(CaptainManager.get().getHealthCheckUuid());
|
||||
});
|
||||
// ************ Beginning of reverse proxy 3rd party services ****************************************
|
||||
app.use(CaptainConstants.netDataRelativePath, function (req, res, next) {
|
||||
if (req.originalUrl.indexOf(CaptainConstants.netDataRelativePath + '/') !==
|
||||
0) {
|
||||
let newUrl = 'https://' +
|
||||
req.get('host') +
|
||||
CaptainConstants.netDataRelativePath +
|
||||
'/';
|
||||
res.redirect(302, newUrl);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
app.use(CaptainConstants.netDataRelativePath, Injector.injectUserUsingCookieDataOnly());
|
||||
app.use(CaptainConstants.netDataRelativePath, function (req, res, next) {
|
||||
if (!InjectionExtractor.extractUserFromInjected(res)) {
|
||||
Logger.e('User not logged in for NetData');
|
||||
res.sendStatus(500);
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
httpProxy.on('error', function (err, req, resOriginal) {
|
||||
if (err) {
|
||||
Logger.e(err);
|
||||
}
|
||||
resOriginal.writeHead(500, {
|
||||
'Content-Type': 'text/plain',
|
||||
});
|
||||
if ((err + '').indexOf('getaddrinfo ENOTFOUND captain-netdata-container') >=
|
||||
0) {
|
||||
resOriginal.end('Something went wrong... err: \n ' +
|
||||
'NetData is not running! Are you sure you have started it?');
|
||||
}
|
||||
else {
|
||||
resOriginal.end('Something went wrong... err: \n ' + (err ? err : 'NULL'));
|
||||
}
|
||||
});
|
||||
app.use(CaptainConstants.netDataRelativePath, function (req, res, next) {
|
||||
httpProxy.web(req, res, {
|
||||
target: 'http://' + CaptainConstants.netDataContainerName + ':19999',
|
||||
});
|
||||
});
|
||||
// ************ End of reverse proxy 3rd party services ****************************************
|
||||
// ********************* Beginning of API End Points *******************************************
|
||||
let API_PREFIX = '/api/';
|
||||
app.use(API_PREFIX + ':apiVersionFromRequest/', function (req, res, next) {
|
||||
if (req.params.apiVersionFromRequest !== CaptainConstants.apiVersion) {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'This captain instance only accepts API ' +
|
||||
CaptainConstants.apiVersion));
|
||||
return;
|
||||
}
|
||||
if (!InjectionExtractor.extractGlobalsFromInjected(res).initialized) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_CAPTAIN_NOT_INITIALIZED, 'Captain is not ready yet...');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
// unsecured end points:
|
||||
app.use(API_PREFIX + CaptainConstants.apiVersion + '/login/', LoginRouter);
|
||||
// secured end points
|
||||
app.use(API_PREFIX + CaptainConstants.apiVersion + '/user/', UserRouter);
|
||||
// ********************* End of API End Points *******************************************
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function (req, res, next) {
|
||||
res.locals.err = new Error('Not Found');
|
||||
res.locals.err.errorStatus = 404;
|
||||
next(res.locals.err);
|
||||
});
|
||||
// error handler
|
||||
app.use(function (err, req, res, next) {
|
||||
Promise.reject(err).catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// Initializing with delay helps with debugging. Usually, docker didn't see the CAPTAIN service
|
||||
// if this was done without a delay
|
||||
setTimeout(function () {
|
||||
CaptainManager.get().initialize();
|
||||
}, 1500);
|
||||
module.exports = app;
|
||||
//# sourceMappingURL=app.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,491 +0,0 @@
|
||||
"use strict";
|
||||
const uuid = require("uuid/v4");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const isValidPath = require('is-valid-path');
|
||||
const APP_DEFINITIONS = 'appDefinitions';
|
||||
function isNameAllowed(name) {
|
||||
const isNameFormattingOk = !!name &&
|
||||
name.length < 50 &&
|
||||
/^[a-z]/.test(name) &&
|
||||
/[a-z0-9]$/.test(name) &&
|
||||
/^[a-z0-9\-]+$/.test(name) &&
|
||||
name.indexOf('--') < 0;
|
||||
return isNameFormattingOk && ['captain', 'registry'].indexOf(name) < 0;
|
||||
}
|
||||
function isPortValid(portNumber) {
|
||||
return portNumber > 0 && portNumber < 65535;
|
||||
}
|
||||
class AppsDataStore {
|
||||
constructor(data, namepace) {
|
||||
this.data = data;
|
||||
this.namepace = namepace;
|
||||
}
|
||||
setEncryptor(encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
saveApp(appName, app) {
|
||||
const self = this;
|
||||
let passwordToBeEncrypted = '';
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!appName) {
|
||||
throw new Error('App Name should not be empty');
|
||||
}
|
||||
let pushWebhook = app.appPushWebhook;
|
||||
if (pushWebhook &&
|
||||
pushWebhook.pushWebhookToken &&
|
||||
pushWebhook.tokenVersion &&
|
||||
pushWebhook.repoInfo &&
|
||||
pushWebhook.repoInfo.repo) {
|
||||
// we have required info
|
||||
passwordToBeEncrypted = pushWebhook.repoInfo.password;
|
||||
pushWebhook.repoInfo.password = '';
|
||||
}
|
||||
else {
|
||||
// some required data is missing. We drop the push data
|
||||
pushWebhook = undefined;
|
||||
}
|
||||
if (app.forceSsl) {
|
||||
let hasAtLeastOneSslDomain = app.hasDefaultSubDomainSsl;
|
||||
const customDomainArray = app.customDomain;
|
||||
if (customDomainArray && customDomainArray.length > 0) {
|
||||
for (let idx = 0; idx < customDomainArray.length; idx++) {
|
||||
if (customDomainArray[idx].hasSsl) {
|
||||
hasAtLeastOneSslDomain = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasAtLeastOneSslDomain) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'Cannot force SSL without at least one SSL-enabled domain!');
|
||||
}
|
||||
}
|
||||
if (app.ports) {
|
||||
for (let i = 0; i < app.ports.length; i++) {
|
||||
const obj = app.ports[i];
|
||||
if (obj.containerPort && obj.hostPort) {
|
||||
const containerPort = Number(obj.containerPort);
|
||||
const hostPort = Number(obj.hostPort);
|
||||
if (!isPortValid(containerPort) ||
|
||||
!isPortValid(hostPort)) {
|
||||
throw new Error(`Invalid ports: ${hostPort} or ${containerPort}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Host or container port is missing');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (app.volumes) {
|
||||
for (let i = 0; i < app.volumes.length; i++) {
|
||||
const obj = app.volumes[i];
|
||||
if (!obj.containerPath ||
|
||||
!(obj.volumeName || obj.hostPath)) {
|
||||
throw new Error('containerPath or the source paths (volume name or host path) are missing');
|
||||
}
|
||||
if (obj.volumeName && obj.hostPath) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Cannot define both host path and volume name!');
|
||||
}
|
||||
if (!isValidPath(obj.containerPath)) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Invalid containerPath: ' + obj.containerPath);
|
||||
}
|
||||
if (obj.hostPath && !isValidPath(obj.hostPath)) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Invalid volume host path: ' + obj.hostPath);
|
||||
}
|
||||
else {
|
||||
if (!obj.volumeName ||
|
||||
!isNameAllowed(obj.volumeName)) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Invalid volume name: ' + obj.volumeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
const appToSave = app;
|
||||
if (passwordToBeEncrypted) {
|
||||
appToSave.appPushWebhook.repoInfo.passwordEncrypted = self.encryptor.encrypt(passwordToBeEncrypted);
|
||||
}
|
||||
return appToSave;
|
||||
})
|
||||
.then(function (appToSave) {
|
||||
self.data.set(APP_DEFINITIONS + '.' + appName, appToSave);
|
||||
});
|
||||
}
|
||||
getServiceName(appName) {
|
||||
return 'srv-' + this.namepace + '--' + appName;
|
||||
}
|
||||
getAppDefinitions() {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
let allApps = self.data.get(APP_DEFINITIONS) || {};
|
||||
let allAppsUnencrypted = {};
|
||||
Object.keys(allApps).forEach(function (appName) {
|
||||
allAppsUnencrypted[appName] = allApps[appName];
|
||||
const appUnencrypted = allAppsUnencrypted[appName];
|
||||
const appSave = allApps[appName];
|
||||
if (appSave.appPushWebhook &&
|
||||
appSave.appPushWebhook.repoInfo &&
|
||||
appSave.appPushWebhook.repoInfo.passwordEncrypted) {
|
||||
const repo = appSave.appPushWebhook.repoInfo;
|
||||
appUnencrypted.appPushWebhook = {
|
||||
tokenVersion: appSave.appPushWebhook.tokenVersion,
|
||||
pushWebhookToken: appSave.appPushWebhook.pushWebhookToken,
|
||||
repoInfo: {
|
||||
repo: repo.repo,
|
||||
user: repo.user,
|
||||
password: self.encryptor.decrypt(repo.passwordEncrypted),
|
||||
branch: repo.branch,
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
resolve(allAppsUnencrypted);
|
||||
});
|
||||
}
|
||||
getAppDefinition(appName) {
|
||||
const self = this;
|
||||
return this.getAppDefinitions().then(function (allApps) {
|
||||
if (!appName) {
|
||||
throw new Error('App Name should not be empty');
|
||||
}
|
||||
const app = allApps[appName];
|
||||
if (!app) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, `App (${appName}) could not be found. Make sure that you have created the app.`);
|
||||
}
|
||||
return app;
|
||||
});
|
||||
}
|
||||
enableSslForDefaultSubDomain(appName) {
|
||||
const self = this;
|
||||
return this.getAppDefinition(appName).then(function (app) {
|
||||
app.hasDefaultSubDomainSsl = true;
|
||||
return self.saveApp(appName, app);
|
||||
});
|
||||
}
|
||||
enableCustomDomainSsl(appName, customDomain) {
|
||||
const self = this;
|
||||
return self.getAppDefinition(appName).then(function (app) {
|
||||
app.customDomain = app.customDomain || [];
|
||||
if (app.customDomain.length > 0) {
|
||||
for (let idx = 0; idx < app.customDomain.length; idx++) {
|
||||
if (app.customDomain[idx].publicDomain === customDomain) {
|
||||
app.customDomain[idx].hasSsl = true;
|
||||
return self.saveApp(appName, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`customDomain: ${customDomain} is not attached to app ${appName}`);
|
||||
});
|
||||
}
|
||||
removeCustomDomainForApp(appName, customDomain) {
|
||||
const self = this;
|
||||
return this.getAppDefinition(appName).then(function (app) {
|
||||
app.customDomain = app.customDomain || [];
|
||||
const newDomains = [];
|
||||
let removed = false;
|
||||
for (let idx = 0; idx < app.customDomain.length; idx++) {
|
||||
if (app.customDomain[idx].publicDomain === customDomain) {
|
||||
removed = true;
|
||||
}
|
||||
else {
|
||||
newDomains.push(app.customDomain[idx]);
|
||||
}
|
||||
}
|
||||
if (!removed) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, `Custom domain ${customDomain} does not exist in ${appName}`);
|
||||
}
|
||||
app.customDomain = newDomains;
|
||||
return self.saveApp(appName, app);
|
||||
});
|
||||
}
|
||||
addCustomDomainForApp(appName, customDomain) {
|
||||
const self = this;
|
||||
return this.getAppDefinition(appName).then(function (app) {
|
||||
app.customDomain = app.customDomain || [];
|
||||
if (app.customDomain.length > 0) {
|
||||
for (let idx = 0; idx < app.customDomain.length; idx++) {
|
||||
if (app.customDomain[idx].publicDomain === customDomain) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, `App already has customDomain: ${customDomain} attached to app ${appName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
app.customDomain.push({
|
||||
publicDomain: customDomain,
|
||||
hasSsl: false,
|
||||
});
|
||||
return self.saveApp(appName, app);
|
||||
});
|
||||
}
|
||||
verifyCustomDomainBelongsToApp(appName, customDomain) {
|
||||
const self = this;
|
||||
return self.getAppDefinition(appName).then(function (app) {
|
||||
app.customDomain = app.customDomain || [];
|
||||
if (app.customDomain.length > 0) {
|
||||
for (let idx = 0; idx < app.customDomain.length; idx++) {
|
||||
if (app.customDomain[idx].publicDomain === customDomain) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, `customDomain ${customDomain} is not attached to app ${appName}`);
|
||||
});
|
||||
}
|
||||
setDeployedVersionAndImage(appName, deployedVersion, builtImage) {
|
||||
if (!appName) {
|
||||
throw new Error('App Name should not be empty');
|
||||
}
|
||||
if (!builtImage || !builtImage.imageName) {
|
||||
throw new Error('ImageName Name should not be empty');
|
||||
}
|
||||
const self = this;
|
||||
return this.getAppDefinition(appName) //
|
||||
.then(function (app) {
|
||||
const versions = app.versions;
|
||||
let found = false;
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
const element = versions[i];
|
||||
if (element.version === deployedVersion) {
|
||||
element.deployedImageName = builtImage.imageName;
|
||||
element.gitHash = builtImage.gitHash;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new Error(`Version trying to deploy not found ${deployedVersion}`);
|
||||
}
|
||||
app.deployedVersion = deployedVersion;
|
||||
return self.saveApp(appName, app);
|
||||
});
|
||||
}
|
||||
createNewVersion(appName) {
|
||||
if (!appName) {
|
||||
throw new Error('App Name should not be empty');
|
||||
}
|
||||
const self = this;
|
||||
return this.getAppDefinition(appName).then(function (app) {
|
||||
const versions = app.versions;
|
||||
let newVersionIndex = versions.length;
|
||||
// Just in case some versions from the db were deleted manually!!!
|
||||
for (let index = 0; index < versions.length; index++) {
|
||||
const element = versions[index];
|
||||
if (newVersionIndex < element.version) {
|
||||
newVersionIndex = element.version + 1;
|
||||
}
|
||||
}
|
||||
versions.push({
|
||||
version: newVersionIndex,
|
||||
gitHash: undefined,
|
||||
timeStamp: new Date().toString(),
|
||||
});
|
||||
return self.saveApp(appName, app).then(function () {
|
||||
return newVersionIndex;
|
||||
});
|
||||
});
|
||||
}
|
||||
setVersionsForMigration(appName, vers, deployedVersion) {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return self.getAppDefinition(appName);
|
||||
})
|
||||
.then(function (appLoaded) {
|
||||
appLoaded.deployedVersion = deployedVersion;
|
||||
appLoaded.versions = vers;
|
||||
return self.saveApp(appName, appLoaded);
|
||||
});
|
||||
}
|
||||
updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, forceSsl, ports, repoInfo, authenticator, customNginxConfig, preDeployFunction) {
|
||||
const self = this;
|
||||
let appObj;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.getAppDefinition(appName);
|
||||
})
|
||||
.then(function (appLoaded) {
|
||||
appObj = appLoaded;
|
||||
if (repoInfo &&
|
||||
repoInfo.repo &&
|
||||
repoInfo.branch &&
|
||||
repoInfo.user &&
|
||||
repoInfo.password) {
|
||||
appObj.appPushWebhook = {
|
||||
tokenVersion: appObj.appPushWebhook &&
|
||||
appObj.appPushWebhook.tokenVersion
|
||||
? appObj.appPushWebhook.tokenVersion
|
||||
: uuid(),
|
||||
pushWebhookToken: appObj.appPushWebhook
|
||||
? appObj.appPushWebhook.pushWebhookToken
|
||||
: '',
|
||||
repoInfo: {
|
||||
repo: repoInfo.repo,
|
||||
user: repoInfo.user,
|
||||
branch: repoInfo.branch,
|
||||
password: repoInfo.password,
|
||||
},
|
||||
};
|
||||
if (appObj.appPushWebhook.pushWebhookToken) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
return authenticator
|
||||
.getAppPushWebhookToken(appName, appObj.appPushWebhook.tokenVersion)
|
||||
.then(function (val) {
|
||||
appObj.appPushWebhook.pushWebhookToken = val;
|
||||
});
|
||||
}
|
||||
else {
|
||||
appObj.appPushWebhook = undefined;
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
instanceCount = Number(instanceCount);
|
||||
if (instanceCount >= 0) {
|
||||
appObj.instanceCount = instanceCount;
|
||||
}
|
||||
appObj.notExposeAsWebApp = !!notExposeAsWebApp;
|
||||
appObj.forceSsl = !!forceSsl;
|
||||
appObj.nodeId = nodeId;
|
||||
appObj.customNginxConfig = customNginxConfig;
|
||||
appObj.preDeployFunction = preDeployFunction;
|
||||
if (ports) {
|
||||
appObj.ports = [];
|
||||
for (let i = 0; i < ports.length; i++) {
|
||||
const obj = ports[i];
|
||||
const containerPort = Number(obj.containerPort);
|
||||
const hostPort = Number(obj.hostPort);
|
||||
appObj.ports.push({
|
||||
hostPort: hostPort,
|
||||
containerPort: containerPort,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (envVars) {
|
||||
appObj.envVars = [];
|
||||
for (let i = 0; i < envVars.length; i++) {
|
||||
const obj = envVars[i];
|
||||
if (obj.key && obj.value) {
|
||||
appObj.envVars.push({
|
||||
key: obj.key,
|
||||
value: obj.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (volumes) {
|
||||
appObj.volumes = [];
|
||||
for (let i = 0; i < volumes.length; i++) {
|
||||
const obj = volumes[i];
|
||||
const newVol = {
|
||||
containerPath: obj.containerPath,
|
||||
};
|
||||
if (obj.hostPath) {
|
||||
newVol.hostPath = obj.hostPath;
|
||||
}
|
||||
else {
|
||||
newVol.volumeName = obj.volumeName;
|
||||
}
|
||||
appObj.volumes.push(newVol);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self.saveApp(appName, appObj);
|
||||
});
|
||||
}
|
||||
deleteAppDefinition(appName) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!isNameAllowed(appName)) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'App Name is not allow. Only lowercase letters and single hyphen is allow'));
|
||||
return;
|
||||
}
|
||||
if (!self.data.get(APP_DEFINITIONS + '.' + appName)) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'App Name does not exist in Database! Cannot be deleted.'));
|
||||
return;
|
||||
}
|
||||
self.data.delete(APP_DEFINITIONS + '.' + appName);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates a new app definition.
|
||||
*
|
||||
* @param appName The appName you want to register
|
||||
* @param hasPersistentData whether the app has persistent data, you can only run one instance of the app.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
registerAppDefinition(appName, hasPersistentData) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!isNameAllowed(appName)) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'App Name is not allow. Only lowercase letters and single hyphen is allow'));
|
||||
return;
|
||||
}
|
||||
if (!!self.data.get(APP_DEFINITIONS + '.' + appName)) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_ALREADY_EXIST, 'App Name already exists. Please use a different name'));
|
||||
return;
|
||||
}
|
||||
const defaultAppDefinition = {
|
||||
hasPersistentData: !!hasPersistentData,
|
||||
instanceCount: 1,
|
||||
networks: [CaptainConstants.captainNetworkName],
|
||||
envVars: [],
|
||||
volumes: [],
|
||||
ports: [],
|
||||
versions: [],
|
||||
deployedVersion: 0,
|
||||
notExposeAsWebApp: false,
|
||||
customDomain: [],
|
||||
hasDefaultSubDomainSsl: false,
|
||||
forceSsl: false,
|
||||
};
|
||||
resolve(defaultAppDefinition);
|
||||
}).then(function (app) {
|
||||
return self.saveApp(appName, app);
|
||||
});
|
||||
}
|
||||
getAppsServerConfig(defaultAppNginxConfig, hasRootSsl, rootDomain) {
|
||||
const self = this;
|
||||
const servers = [];
|
||||
return self.getAppDefinitions().then(function (apps) {
|
||||
Object.keys(apps).forEach(function (appName) {
|
||||
const webApp = apps[appName];
|
||||
if (webApp.notExposeAsWebApp) {
|
||||
return;
|
||||
}
|
||||
const localDomain = self.getServiceName(appName);
|
||||
const forceSsl = !!webApp.forceSsl;
|
||||
const nginxConfigTemplate = webApp.customNginxConfig || defaultAppNginxConfig;
|
||||
const serverWithSubDomain = {};
|
||||
serverWithSubDomain.hasSsl =
|
||||
hasRootSsl && webApp.hasDefaultSubDomainSsl;
|
||||
serverWithSubDomain.publicDomain = appName + '.' + rootDomain;
|
||||
serverWithSubDomain.localDomain = localDomain;
|
||||
serverWithSubDomain.forceSsl = forceSsl;
|
||||
serverWithSubDomain.nginxConfigTemplate = nginxConfigTemplate;
|
||||
servers.push(serverWithSubDomain);
|
||||
// adding custom domains
|
||||
const customDomainArray = webApp.customDomain;
|
||||
if (customDomainArray && customDomainArray.length > 0) {
|
||||
for (let idx = 0; idx < customDomainArray.length; idx++) {
|
||||
const d = customDomainArray[idx];
|
||||
servers.push({
|
||||
hasSsl: d.hasSsl,
|
||||
forceSsl: forceSsl,
|
||||
publicDomain: d.publicDomain,
|
||||
localDomain: localDomain,
|
||||
nginxConfigTemplate: nginxConfigTemplate,
|
||||
staticWebRoot: '',
|
||||
customErrorPagesDirectory: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return servers;
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = AppsDataStore;
|
||||
//# sourceMappingURL=AppsDataStore.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,234 +0,0 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Created by kasra on 27/06/17.
|
||||
*/
|
||||
const Configstore = require("configstore");
|
||||
const fs = require("fs-extra");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const Encryptor = require("../utils/Encryptor");
|
||||
const AppsDataStore = require("./AppsDataStore");
|
||||
const RegistriesDataStore = require("./RegistriesDataStore");
|
||||
// keys:
|
||||
const NAMESPACE = 'namespace';
|
||||
const HASHED_PASSWORD = 'hashedPassword';
|
||||
const CUSTOM_DOMAIN = 'customDomain';
|
||||
const HAS_ROOT_SSL = 'hasRootSsl';
|
||||
const FORCE_ROOT_SSL = 'forceRootSsl';
|
||||
const HAS_REGISTRY_SSL = 'hasRegistrySsl';
|
||||
const EMAIL_ADDRESS = 'emailAddress';
|
||||
const NET_DATA_INFO = 'netDataInfo';
|
||||
const NGINX_BASE_CONFIG = 'nginxBaseConfig';
|
||||
const NGINX_CAPTAIN_CONFIG = 'nginxCaptainConfig';
|
||||
const DEFAULT_CAPTAIN_ROOT_DOMAIN = 'captain.localhost';
|
||||
const DEFAULT_NGINX_BASE_CONFIG = fs
|
||||
.readFileSync(__dirname + '/../../template/base-nginx-conf.ejs')
|
||||
.toString();
|
||||
const DEFAULT_NGINX_CAPTAIN_CONFIG = fs
|
||||
.readFileSync(__dirname + '/../../template/root-nginx-conf.ejs')
|
||||
.toString();
|
||||
const DEFAULT_NGINX_CONFIG_FOR_APP = fs
|
||||
.readFileSync(__dirname + '/../../template/server-block-conf.ejs')
|
||||
.toString();
|
||||
class DataStore {
|
||||
constructor(namespace) {
|
||||
const data = new Configstore(`captain-store-${namespace}`, // This value seems to be unused
|
||||
{}, {
|
||||
configPath: `${CaptainConstants.captainDataDirectory}/config-${namespace}.json`,
|
||||
});
|
||||
this.data = data;
|
||||
this.namespace = namespace;
|
||||
this.data.set(NAMESPACE, namespace);
|
||||
this.appsDataStore = new AppsDataStore(this.data, namespace);
|
||||
this.registriesDataStore = new RegistriesDataStore(this.data, namespace);
|
||||
}
|
||||
setEncryptionSalt(salt) {
|
||||
this.encryptor = new Encryptor.CaptainEncryptor(this.namespace + salt);
|
||||
this.appsDataStore.setEncryptor(this.encryptor);
|
||||
this.registriesDataStore.setEncryptor(this.encryptor);
|
||||
}
|
||||
getNameSpace() {
|
||||
return this.data.get(NAMESPACE);
|
||||
}
|
||||
setHashedPassword(newHashedPassword) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.data.set(HASHED_PASSWORD, newHashedPassword);
|
||||
});
|
||||
}
|
||||
getHashedPassword() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.data.get(HASHED_PASSWORD);
|
||||
});
|
||||
}
|
||||
/*
|
||||
"smtp": {
|
||||
"to": "",
|
||||
"hostname": "",
|
||||
"server": "",
|
||||
"port": "",
|
||||
"allowNonTls": false,
|
||||
"password": "",
|
||||
"username": ""
|
||||
},
|
||||
"slack": {
|
||||
"hook": "",
|
||||
"channel": ""
|
||||
},
|
||||
"telegram": {
|
||||
"botToken": "",
|
||||
"chatId": ""
|
||||
},
|
||||
"pushBullet": {
|
||||
"fallbackEmail": "",
|
||||
"apiToken": ""
|
||||
}
|
||||
*/
|
||||
getNetDataInfo() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
const netDataInfo = self.data.get(NET_DATA_INFO) || {};
|
||||
netDataInfo.isEnabled = netDataInfo.isEnabled || false;
|
||||
netDataInfo.data = netDataInfo.data || {};
|
||||
netDataInfo.data.smtp = netDataInfo.data.smtp || {};
|
||||
netDataInfo.data.slack = netDataInfo.data.slack || {};
|
||||
netDataInfo.data.telegram = netDataInfo.data.telegram || {};
|
||||
netDataInfo.data.pushBullet = netDataInfo.data.pushBullet || {};
|
||||
return netDataInfo;
|
||||
});
|
||||
}
|
||||
setNetDataInfo(netDataInfo) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.data.set(NET_DATA_INFO, netDataInfo);
|
||||
});
|
||||
}
|
||||
getBuiltImageNameBase(appName) {
|
||||
return 'img-' + this.getNameSpace() + '--' + appName;
|
||||
}
|
||||
getRootDomain() {
|
||||
return this.data.get(CUSTOM_DOMAIN) || DEFAULT_CAPTAIN_ROOT_DOMAIN;
|
||||
}
|
||||
hasCustomDomain() {
|
||||
return !!this.data.get(CUSTOM_DOMAIN);
|
||||
}
|
||||
getServerList() {
|
||||
const self = this;
|
||||
let hasRootSsl;
|
||||
let rootDomain;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.getHasRootSsl();
|
||||
})
|
||||
.then(function (val) {
|
||||
hasRootSsl = val;
|
||||
return self.getRootDomain();
|
||||
})
|
||||
.then(function (val) {
|
||||
rootDomain = val;
|
||||
})
|
||||
.then(function () {
|
||||
return self.getDefaultAppNginxConfig();
|
||||
})
|
||||
.then(function (defaultAppNginxConfig) {
|
||||
return self
|
||||
.getAppsDataStore()
|
||||
.getAppsServerConfig(defaultAppNginxConfig, hasRootSsl, rootDomain);
|
||||
});
|
||||
}
|
||||
getAppsDataStore() {
|
||||
return this.appsDataStore;
|
||||
}
|
||||
getRegistriesDataStore() {
|
||||
return this.registriesDataStore;
|
||||
}
|
||||
setUserEmailAddress(emailAddress) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.data.set(EMAIL_ADDRESS, emailAddress);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
getUserEmailAddress() {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
resolve(self.data.get(EMAIL_ADDRESS));
|
||||
});
|
||||
}
|
||||
setHasRootSsl(hasRootSsl) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.data.set(HAS_ROOT_SSL, hasRootSsl);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
setForceSsl(forceSsl) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.data.set(FORCE_ROOT_SSL, forceSsl);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
getForceSsl() {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
resolve(!!self.data.get(FORCE_ROOT_SSL));
|
||||
});
|
||||
}
|
||||
setHasRegistrySsl(hasRegistrySsl) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.data.set(HAS_REGISTRY_SSL, hasRegistrySsl);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
getDefaultAppNginxConfig() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return DEFAULT_NGINX_CONFIG_FOR_APP;
|
||||
});
|
||||
}
|
||||
getNginxConfig() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return {
|
||||
baseConfig: {
|
||||
byDefault: DEFAULT_NGINX_BASE_CONFIG,
|
||||
customValue: self.data.get(NGINX_BASE_CONFIG),
|
||||
},
|
||||
captainConfig: {
|
||||
byDefault: DEFAULT_NGINX_CAPTAIN_CONFIG,
|
||||
customValue: self.data.get(NGINX_CAPTAIN_CONFIG),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
setNginxConfig(baseConfig, captainConfig) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
self.data.set(NGINX_BASE_CONFIG, baseConfig);
|
||||
self.data.set(NGINX_CAPTAIN_CONFIG, captainConfig);
|
||||
});
|
||||
}
|
||||
getHasRootSsl() {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
resolve(self.data.get(HAS_ROOT_SSL));
|
||||
});
|
||||
}
|
||||
getHasRegistrySsl() {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
resolve(!!self.data.get(HAS_REGISTRY_SSL));
|
||||
});
|
||||
}
|
||||
setCustomDomain(customDomain) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.data.set(CUSTOM_DOMAIN, customDomain);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = DataStore;
|
||||
//# sourceMappingURL=DataStore.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,18 +0,0 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Created by kasra on 27/06/17.
|
||||
*/
|
||||
const DataStore = require("./DataStore");
|
||||
const dataStoreCache = {};
|
||||
module.exports = {
|
||||
getDataStore: function (namespace) {
|
||||
if (!namespace) {
|
||||
throw new Error('NameSpace is empty');
|
||||
}
|
||||
if (!dataStoreCache[namespace]) {
|
||||
dataStoreCache[namespace] = new DataStore(namespace);
|
||||
}
|
||||
return dataStoreCache[namespace];
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=DataStoreProvider.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"DataStoreProvider.js","sourceRoot":"","sources":["../../src/datastore/DataStoreProvider.ts"],"names":[],"mappings":";AAAA;;GAEG;AAEH,yCAAyC;AAEzC,MAAM,cAAc,GAA+B,EAAE,CAAA;AAErD,iBAAS;IACL,YAAY,EAAE,UAAS,SAAiB;QACpC,IAAI,CAAC,SAAS,EAAE;YACZ,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;SACxC;QAED,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE;YAC5B,cAAc,CAAC,SAAS,CAAC,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,CAAA;SACvD;QAED,OAAO,cAAc,CAAC,SAAS,CAAC,CAAA;IACpC,CAAC;CACJ,CAAA"}
|
||||
@@ -1,200 +0,0 @@
|
||||
"use strict";
|
||||
const uuid = require("uuid/v4");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const IRegistryInfo_1 = require("../models/IRegistryInfo");
|
||||
const DOCKER_REGISTRIES = 'dockerRegistries';
|
||||
const DEFAULT_DOCKER_REGISTRY_ID = 'defaultDockerRegId';
|
||||
class RegistriesDataStore {
|
||||
constructor(data, namepace) {
|
||||
this.data = data;
|
||||
this.namepace = namepace;
|
||||
}
|
||||
setEncryptor(encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
getDefaultPushRegistryId() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.data.get(DEFAULT_DOCKER_REGISTRY_ID);
|
||||
});
|
||||
}
|
||||
setDefaultPushRegistryId(registryId) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
let found = false;
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
const registry = registries[i];
|
||||
if (registry.id === registryId) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
// registryId can be NULL/Empty, meaning that no registry will be the default push registry
|
||||
if (!found && !!registryId) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.NOT_FOUND, 'Registry not found');
|
||||
}
|
||||
self.data.set(DEFAULT_DOCKER_REGISTRY_ID, registryId);
|
||||
});
|
||||
}
|
||||
getRegistryById(registryId) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!registryId)
|
||||
throw new Error('Empty registry id!');
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
const registry = registries[i];
|
||||
if (registry.id === registryId) {
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.NOT_FOUND, 'Registry not found');
|
||||
});
|
||||
}
|
||||
deleteRegistry(registryId) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!registryId)
|
||||
throw new Error('Empty registry id to delete!');
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
const newReg = [];
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
const registry = registries[i];
|
||||
if (registry.id !== registryId) {
|
||||
newReg.push(registry);
|
||||
}
|
||||
}
|
||||
if (newReg.length === registries.length) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.NOT_FOUND, 'Registry not found');
|
||||
}
|
||||
self.saveAllRegistries(newReg);
|
||||
});
|
||||
}
|
||||
getAllRegistries() {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.data.get(DOCKER_REGISTRIES) || [];
|
||||
})
|
||||
.then(function (registries) {
|
||||
const unencryptedList = [];
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
const element = registries[i];
|
||||
unencryptedList.push({
|
||||
id: element.id,
|
||||
registryDomain: element.registryDomain,
|
||||
registryImagePrefix: element.registryImagePrefix,
|
||||
registryUser: element.registryUser,
|
||||
registryPassword: self.encryptor.decrypt(element.registryPasswordEncrypted),
|
||||
registryType: element.registryType,
|
||||
});
|
||||
}
|
||||
return unencryptedList;
|
||||
});
|
||||
}
|
||||
updateRegistry(id, registryUser, registryPassword, registryDomain, registryImagePrefix) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!id ||
|
||||
!registryUser ||
|
||||
!registryPassword ||
|
||||
!registryDomain) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'User, password and domain are required.');
|
||||
}
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
let found = false;
|
||||
for (let idx = 0; idx < registries.length; idx++) {
|
||||
const element = registries[idx];
|
||||
if (element.id === id) {
|
||||
if (element.registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'You cannot edit self-hosted registry');
|
||||
}
|
||||
element.registryUser = registryUser;
|
||||
element.registryPassword = registryPassword;
|
||||
element.registryDomain = registryDomain;
|
||||
element.registryImagePrefix = registryImagePrefix;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.NOT_FOUND, 'Registry ID not found');
|
||||
return self.saveAllRegistries(registries);
|
||||
});
|
||||
}
|
||||
addRegistryToDb(registryUser, registryPassword, registryDomain, registryImagePrefix, registryType) {
|
||||
const self = this;
|
||||
let savedId = undefined;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!registryUser ||
|
||||
!registryPassword ||
|
||||
!registryDomain ||
|
||||
!registryType) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'User, password and domain are required.');
|
||||
}
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
let id = uuid();
|
||||
let isAlreadyTaken = true;
|
||||
while (isAlreadyTaken) {
|
||||
id = uuid();
|
||||
isAlreadyTaken = false;
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
if (registries[i].id === id) {
|
||||
isAlreadyTaken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
savedId = id;
|
||||
registries.push({
|
||||
id,
|
||||
registryUser,
|
||||
registryPassword,
|
||||
registryDomain,
|
||||
registryImagePrefix,
|
||||
registryType,
|
||||
});
|
||||
return self.saveAllRegistries(registries);
|
||||
})
|
||||
.then(function () {
|
||||
if (!savedId)
|
||||
throw new Error('Saved registry, but ID is null. This should never happen');
|
||||
return savedId;
|
||||
});
|
||||
}
|
||||
saveAllRegistries(registries) {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
const encryptedList = [];
|
||||
for (let i = 0; i < registries.length; i++) {
|
||||
const element = registries[i];
|
||||
encryptedList.push({
|
||||
id: element.id,
|
||||
registryDomain: element.registryDomain,
|
||||
registryImagePrefix: element.registryImagePrefix,
|
||||
registryUser: element.registryUser,
|
||||
registryPasswordEncrypted: self.encryptor.encrypt(element.registryPassword),
|
||||
registryType: element.registryType,
|
||||
});
|
||||
}
|
||||
self.data.set(DOCKER_REGISTRIES, encryptedList);
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = RegistriesDataStore;
|
||||
//# sourceMappingURL=RegistriesDataStore.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,24 +0,0 @@
|
||||
"use strict";
|
||||
class InjectionExtractor {
|
||||
static extractUserFromInjected(res) {
|
||||
return {
|
||||
user: res.locals.user,
|
||||
};
|
||||
}
|
||||
static extractGlobalsFromInjected(res) {
|
||||
return {
|
||||
initialized: res.locals.initialized,
|
||||
namespace: res.locals.namespace,
|
||||
forceSsl: res.locals.forceSsl,
|
||||
};
|
||||
}
|
||||
static extractAppAndUserForWebhook(res) {
|
||||
return {
|
||||
user: res.locals.user,
|
||||
appName: res.locals.namespace,
|
||||
app: res.locals.app,
|
||||
};
|
||||
}
|
||||
}
|
||||
module.exports = InjectionExtractor;
|
||||
//# sourceMappingURL=InjectionExtractor.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"InjectionExtractor.js","sourceRoot":"","sources":["../../src/injection/InjectionExtractor.ts"],"names":[],"mappings":";AAGA,MAAM,kBAAkB;IACpB,MAAM,CAAC,uBAAuB,CAAC,GAAa;QACxC,OAAO;YACH,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAoB;SACxC,CAAA;IACL,CAAC;IAED,MAAM,CAAC,0BAA0B,CAAC,GAAa;QAC3C,OAAO;YACH,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,WAAsB;YAC9C,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAmB;YACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,QAAmB;SAC3C,CAAA;IACL,CAAC;IACD,MAAM,CAAC,2BAA2B,CAAC,GAAa;QAC5C,OAAO;YACH,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAoB;YACrC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,SAAmB;YACvC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAc;SACjC,CAAA;IACL,CAAC;CACJ;AAED,iBAAS,kBAAkB,CAAA"}
|
||||
@@ -1,147 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const DataStoreProvider = require("../datastore/DataStoreProvider");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const CaptainManager = require("../user/system/CaptainManager");
|
||||
const ServiceManager = require("../user/ServiceManager");
|
||||
const DockerApi_1 = require("../docker/DockerApi");
|
||||
const BaseApi = require("../api/BaseApi");
|
||||
const Logger = require("../utils/Logger");
|
||||
const InjectionExtractor = require("./InjectionExtractor");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const dockerApi = DockerApi_1.default.get();
|
||||
const serviceMangerCache = {};
|
||||
/**
|
||||
* Global dependency injection module
|
||||
*/
|
||||
function injectGlobal() {
|
||||
return function (req, res, next) {
|
||||
const locals = res.locals;
|
||||
locals.initialized = CaptainManager.get().isInitialized();
|
||||
locals.namespace = req.header(CaptainConstants.headerNamespace);
|
||||
locals.forceSsl = CaptainManager.get().getForceSslValue();
|
||||
if (locals.namespace &&
|
||||
locals.namespace !== CaptainConstants.rootNameSpace) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Namespace unknown');
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
exports.injectGlobal = injectGlobal;
|
||||
/**
|
||||
* User dependency injection module
|
||||
*/
|
||||
function injectUser() {
|
||||
return function (req, res, next) {
|
||||
if (InjectionExtractor.extractUserFromInjected(res).user) {
|
||||
next();
|
||||
return; // user is already injected by another layer
|
||||
}
|
||||
const namespace = res.locals.namespace;
|
||||
CaptainManager.getAuthenticator(namespace)
|
||||
.decodeAuthToken(req.header(CaptainConstants.headerAuth) || '')
|
||||
.then(function (userDecoded) {
|
||||
if (userDecoded) {
|
||||
const datastore = DataStoreProvider.getDataStore(namespace);
|
||||
if (!serviceMangerCache[namespace]) {
|
||||
serviceMangerCache[namespace] = new ServiceManager(datastore, dockerApi, CaptainManager.get().getLoadBalanceManager());
|
||||
}
|
||||
const user = {
|
||||
namespace: namespace,
|
||||
dataStore: datastore,
|
||||
serviceManager: serviceMangerCache[namespace],
|
||||
initialized: serviceMangerCache[namespace].isInited(),
|
||||
};
|
||||
res.locals.user = user;
|
||||
}
|
||||
next();
|
||||
})
|
||||
.catch(function (error) {
|
||||
if (error && error.captainErrorType) {
|
||||
res.send(new BaseApi(error.captainErrorType, error.apiMessage));
|
||||
return;
|
||||
}
|
||||
Logger.e(error);
|
||||
res.locals.user = undefined;
|
||||
next();
|
||||
});
|
||||
};
|
||||
}
|
||||
exports.injectUser = injectUser;
|
||||
/**
|
||||
* A pseudo user injection. Only used for webhooks. Can only trigger certain actions.
|
||||
*/
|
||||
function injectUserForWebhook() {
|
||||
return function (req, res, next) {
|
||||
const token = req.query.token;
|
||||
const namespace = req.query.namespace;
|
||||
let app = undefined;
|
||||
if (!token || !namespace) {
|
||||
Logger.e('Trigger build is called with no token/namespace');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const dataStore = DataStoreProvider.getDataStore(namespace);
|
||||
let decodedInfo;
|
||||
CaptainManager.getAuthenticator(namespace)
|
||||
.decodeAppPushWebhookToken(token)
|
||||
.then(function (data) {
|
||||
decodedInfo = data;
|
||||
return dataStore
|
||||
.getAppsDataStore()
|
||||
.getAppDefinition(decodedInfo.appName);
|
||||
})
|
||||
.then(function (appFound) {
|
||||
app = appFound;
|
||||
if (app.appPushWebhook &&
|
||||
app.appPushWebhook.tokenVersion !== decodedInfo.tokenVersion) {
|
||||
throw new Error('Token Info do not match');
|
||||
}
|
||||
const datastore = DataStoreProvider.getDataStore(namespace);
|
||||
if (!serviceMangerCache[namespace]) {
|
||||
serviceMangerCache[namespace] = new ServiceManager(datastore, dockerApi, CaptainManager.get().getLoadBalanceManager());
|
||||
}
|
||||
const user = {
|
||||
namespace: namespace,
|
||||
dataStore: datastore,
|
||||
serviceManager: serviceMangerCache[namespace],
|
||||
initialized: serviceMangerCache[namespace].isInited(),
|
||||
};
|
||||
res.locals.user = user;
|
||||
res.locals.app = app;
|
||||
res.locals.appName = decodedInfo.appName;
|
||||
next();
|
||||
})
|
||||
.catch(function (error) {
|
||||
Logger.e(error);
|
||||
res.locals.app = undefined;
|
||||
next();
|
||||
});
|
||||
};
|
||||
}
|
||||
exports.injectUserForWebhook = injectUserForWebhook;
|
||||
/**
|
||||
* User dependency injection module. This is a less secure way for user injection. But for reverse proxy services,
|
||||
* this is the only way that we can secure the call
|
||||
*/
|
||||
function injectUserUsingCookieDataOnly() {
|
||||
return function (req, res, next) {
|
||||
CaptainManager.getAuthenticator(CaptainConstants.rootNameSpace)
|
||||
.decodeAuthTokenFromCookies(req.cookies[CaptainConstants.headerCookieAuth])
|
||||
.then(function (user) {
|
||||
res.locals.user = user;
|
||||
next();
|
||||
})
|
||||
.catch(function (error) {
|
||||
if (error && error.captainErrorType) {
|
||||
res.send(new BaseApi(error.captainErrorType, error.apiMessage));
|
||||
return;
|
||||
}
|
||||
Logger.e(error);
|
||||
res.locals.user = undefined;
|
||||
next();
|
||||
});
|
||||
};
|
||||
}
|
||||
exports.injectUserUsingCookieDataOnly = injectUserUsingCookieDataOnly;
|
||||
//# sourceMappingURL=Injector.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Injector.js","sourceRoot":"","sources":["../../src/injection/Injector.ts"],"names":[],"mappings":";;AAAA,oEAAoE;AAEpE,8DAA8D;AAC9D,gEAAgE;AAChE,yDAAyD;AACzD,mDAAmD;AACnD,0CAA0C;AAE1C,0CAA0C;AAG1C,2DAA2D;AAC3D,wDAAwD;AAExD,MAAM,SAAS,GAAG,mBAAiB,CAAC,GAAG,EAAE,CAAA;AAEzC,MAAM,kBAAkB,GAAG,EAAqC,CAAA;AAEhE;;GAEG;AACH,SAAgB,YAAY;IACxB,OAAO,UAAS,GAAY,EAAE,GAAa,EAAE,IAAkB;QAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;QAEzB,MAAM,CAAC,WAAW,GAAG,cAAc,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAA;QACzD,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAA;QAC/D,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAA;QAEzD,IACI,MAAM,CAAC,SAAS;YAChB,MAAM,CAAC,SAAS,KAAK,gBAAgB,CAAC,aAAa,EACrD;YACE,MAAM,cAAc,CAAC,WAAW,CAC5B,cAAc,CAAC,oBAAoB,EACnC,mBAAmB,CACtB,CAAA;SACJ;QAED,IAAI,EAAE,CAAA;IACV,CAAC,CAAA;AACL,CAAC;AApBD,oCAoBC;AAED;;GAEG;AACH,SAAgB,UAAU;IACtB,OAAO,UAAS,GAAY,EAAE,GAAa,EAAE,IAAkB;QAC3D,IAAI,kBAAkB,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YACtD,IAAI,EAAE,CAAA;YACN,OAAM,CAAC,4CAA4C;SACtD;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAA;QAEtC,cAAc,CAAC,gBAAgB,CAAC,SAAS,CAAC;aACrC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;aAC9D,IAAI,CAAC,UAAS,WAAW;YACtB,IAAI,WAAW,EAAE;gBACb,MAAM,SAAS,GAAG,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;gBAC3D,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE;oBAChC,kBAAkB,CAAC,SAAS,CAAC,GAAG,IAAI,cAAc,CAC9C,SAAS,EACT,SAAS,EACT,cAAc,CAAC,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAC/C,CAAA;iBACJ;gBACD,MAAM,IAAI,GAA2B;oBACjC,SAAS,EAAE,SAAS;oBACpB,SAAS,EAAE,SAAS;oBACpB,cAAc,EAAE,kBAAkB,CAAC,SAAS,CAAC;oBAC7C,WAAW,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;iBACxD,CAAA;gBACD,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;aACzB;YAED,IAAI,EAAE,CAAA;QACV,CAAC,CAAC;aACD,KAAK,CAAC,UAAS,KAAmB;YAC/B,IAAI,KAAK,IAAI,KAAK,CAAC,gBAAgB,EAAE;gBACjC,GAAG,CAAC,IAAI,CACJ,IAAI,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,UAAU,CAAC,CACxD,CAAA;gBACD,OAAM;aACT;YACD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YACf,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,CAAA;YAC3B,IAAI,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;IACV,CAAC,CAAA;AACL,CAAC;AA5CD,gCA4CC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAChC,OAAO,UAAS,GAAY,EAAE,GAAa,EAAE,IAAkB;QAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAA;QAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAA;QACrC,IAAI,GAAG,GAAG,SAAS,CAAA;QAEnB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE;YACtB,MAAM,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAA;YAC3D,IAAI,EAAE,CAAA;YACN,OAAM;SACT;QAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;QAE3D,IAAI,WAAuC,CAAA;QAE3C,cAAc,CAAC,gBAAgB,CAAC,SAAS,CAAC;aACrC,yBAAyB,CAAC,KAAK,CAAC;aAChC,IAAI,CAAC,UAAS,IAAI;YACf,WAAW,GAAG,IAAI,CAAA;YAElB,OAAO,SAAS;iBACX,gBAAgB,EAAE;iBAClB,gBAAgB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAC9C,CAAC,CAAC;aACD,IAAI,CAAC,UAAS,QAAQ;YACnB,GAAG,GAAG,QAAQ,CAAA;YAEd,IACI,GAAG,CAAC,cAAc;gBAClB,GAAG,CAAC,cAAc,CAAC,YAAY,KAAK,WAAW,CAAC,YAAY,EAC9D;gBACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;aAC7C;YAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;YAE3D,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE;gBAChC,kBAAkB,CAAC,SAAS,CAAC,GAAG,IAAI,cAAc,CAC9C,SAAS,EACT,SAAS,EACT,cAAc,CAAC,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAC/C,CAAA;aACJ;YAED,MAAM,IAAI,GAA2B;gBACjC,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,SAAS;gBACpB,cAAc,EAAE,kBAAkB,CAAC,SAAS,CAAC;gBAC7C,WAAW,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;aACxD,CAAA;YAED,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;YACtB,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,CAAA;YACpB,GAAG,CAAC,MAAM,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAA;YAExC,IAAI,EAAE,CAAA;QACV,CAAC,CAAC;aACD,KAAK,CAAC,UAAS,KAAK;YACjB,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,CAAA;YAC1B,IAAI,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;IACV,CAAC,CAAA;AACL,CAAC;AAhED,oDAgEC;AAED;;;GAGG;AACH,SAAgB,6BAA6B;IACzC,OAAO,UAAS,GAAY,EAAE,GAAa,EAAE,IAAkB;QAC3D,cAAc,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,aAAa,CAAC;aAC1D,0BAA0B,CACvB,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CACjD;aACA,IAAI,CAAC,UAAS,IAAI;YACf,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAA;YAEtB,IAAI,EAAE,CAAA;QACV,CAAC,CAAC;aACD,KAAK,CAAC,UAAS,KAAK;YACjB,IAAI,KAAK,IAAI,KAAK,CAAC,gBAAgB,EAAE;gBACjC,GAAG,CAAC,IAAI,CACJ,IAAI,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,UAAU,CAAC,CACxD,CAAA;gBACD,OAAM;aACT;YACD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YACf,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,CAAA;YAC3B,IAAI,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;IACV,CAAC,CAAA;AACL,CAAC;AAvBD,sEAuBC"}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=AppDefinition.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"AppDefinition.js","sourceRoot":"","sources":["../../src/models/AppDefinition.ts"],"names":[],"mappings":""}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=DockerAuthObj.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"DockerAuthObj.js","sourceRoot":"","sources":["../../src/models/DockerAuthObj.ts"],"names":[],"mappings":""}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=DockerSecret.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"DockerSecret.js","sourceRoot":"","sources":["../../src/models/DockerSecret.ts"],"names":[],"mappings":""}
|
||||
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=DockerService.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"DockerService.js","sourceRoot":"","sources":["../../src/models/DockerService.ts"],"names":[],"mappings":""}
|
||||
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=IBuiltImage.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"IBuiltImage.js","sourceRoot":"","sources":["../../src/models/IBuiltImage.ts"],"names":[],"mappings":""}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=ICacheGeneric.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"ICacheGeneric.js","sourceRoot":"","sources":["../../src/models/ICacheGeneric.ts"],"names":[],"mappings":""}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=ICaptainDefinition.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"ICaptainDefinition.js","sourceRoot":"","sources":["../../src/models/ICaptainDefinition.ts"],"names":[],"mappings":""}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=IImageSource.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"IImageSource.js","sourceRoot":"","sources":["../../src/models/IImageSource.ts"],"names":[],"mappings":""}
|
||||
@@ -1,8 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class IRegistryTypes {
|
||||
}
|
||||
IRegistryTypes.LOCAL_REG = 'LOCAL_REG';
|
||||
IRegistryTypes.REMOTE_REG = 'REMOTE_REG';
|
||||
exports.IRegistryTypes = IRegistryTypes;
|
||||
//# sourceMappingURL=IRegistryInfo.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"IRegistryInfo.js","sourceRoot":"","sources":["../../src/models/IRegistryInfo.ts"],"names":[],"mappings":";;AAAA,MAAa,cAAc;;AACP,wBAAS,GAAG,WAAW,CAAA;AACvB,yBAAU,GAAG,YAAY,CAAA;AAF7C,wCAGC"}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=IServerBlockDetails.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"IServerBlockDetails.js","sourceRoot":"","sources":["../../src/models/IServerBlockDetails.ts"],"names":[],"mappings":""}
|
||||
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=InjectionInterfaces.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"InjectionInterfaces.js","sourceRoot":"","sources":["../../src/models/InjectionInterfaces.ts"],"names":[],"mappings":""}
|
||||
@@ -1,6 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class LoadBalancerInfo {
|
||||
}
|
||||
exports.default = LoadBalancerInfo;
|
||||
//# sourceMappingURL=LoadBalancerInfo.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"LoadBalancerInfo.js","sourceRoot":"","sources":["../../src/models/LoadBalancerInfo.ts"],"names":[],"mappings":";;AAAA,MAAqB,gBAAgB;CAUpC;AAVD,mCAUC"}
|
||||
@@ -1,3 +0,0 @@
|
||||
class NetDataInfo {
|
||||
}
|
||||
//# sourceMappingURL=NetDataInfo.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"NetDataInfo.js","sourceRoot":"","sources":["../../src/models/NetDataInfo.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW;CAyBhB"}
|
||||
@@ -1,8 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class VolumesTypes {
|
||||
}
|
||||
VolumesTypes.BIND = 'bind';
|
||||
VolumesTypes.VOLUME = 'volume';
|
||||
exports.VolumesTypes = VolumesTypes;
|
||||
//# sourceMappingURL=OtherTypes.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"OtherTypes.js","sourceRoot":"","sources":["../../src/models/OtherTypes.ts"],"names":[],"mappings":";;AAKA,MAAsB,YAAY;;AACP,iBAAI,GAAG,MAAM,CAAA;AACb,mBAAM,GAAG,QAAQ,CAAA;AAF5C,oCAGC"}
|
||||
@@ -1 +0,0 @@
|
||||
//# sourceMappingURL=ServerDockerInfo.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"ServerDockerInfo.js","sourceRoot":"","sources":["../../src/models/ServerDockerInfo.ts"],"names":[],"mappings":""}
|
||||
@@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=UserJwt.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"UserJwt.js","sourceRoot":"","sources":["../../src/models/UserJwt.ts"],"names":[],"mappings":""}
|
||||
@@ -1,42 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../api/BaseApi");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const InjectionExtractor = require("../injection/InjectionExtractor");
|
||||
const DataStoreProvider = require("../datastore/DataStoreProvider");
|
||||
const CaptainManager = require("../user/system/CaptainManager");
|
||||
const router = express.Router();
|
||||
router.post('/', function (req, res, next) {
|
||||
let password = req.body.password || '';
|
||||
if (!password) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'password is empty.');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
let authToken;
|
||||
const namespace = InjectionExtractor.extractGlobalsFromInjected(res)
|
||||
.namespace;
|
||||
let loadedHashedPassword = '';
|
||||
Promise.resolve() //
|
||||
.then(function () {
|
||||
return DataStoreProvider.getDataStore(namespace).getHashedPassword();
|
||||
})
|
||||
.then(function (savedHashedPassword) {
|
||||
loadedHashedPassword = savedHashedPassword;
|
||||
return CaptainManager.getAuthenticator(namespace).getAuthToken(password, loadedHashedPassword);
|
||||
})
|
||||
.then(function (token) {
|
||||
authToken = token;
|
||||
return CaptainManager.getAuthenticator(namespace).getAuthTokenForCookies(password, loadedHashedPassword);
|
||||
})
|
||||
.then(function (cookieAuth) {
|
||||
res.cookie(CaptainConstants.headerCookieAuth, cookieAuth);
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Login succeeded');
|
||||
baseApi.data = { token: authToken };
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=LoginRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"LoginRouter.js","sourceRoot":"","sources":["../../src/routes/LoginRouter.ts"],"names":[],"mappings":";AAAA,mCAAmC;AACnC,0CAA0C;AAC1C,wDAAwD;AACxD,8DAA8D;AAC9D,sEAAsE;AACtE,oEAAoE;AACpE,gEAAgE;AAEhE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAA;AAE/B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAS,GAAG,EAAE,GAAG,EAAE,IAAI;IACpC,IAAI,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAA;IAEtC,IAAI,CAAC,QAAQ,EAAE;QACX,IAAI,QAAQ,GAAG,IAAI,OAAO,CACtB,cAAc,CAAC,oBAAoB,EACnC,oBAAoB,CACvB,CAAA;QACD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAClB,OAAM;KACT;IAED,IAAI,SAAiB,CAAA;IAErB,MAAM,SAAS,GAAG,kBAAkB,CAAC,0BAA0B,CAAC,GAAG,CAAC;SAC/D,SAAS,CAAA;IAEd,IAAI,oBAAoB,GAAG,EAAE,CAAA;IAE7B,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE;SACf,IAAI,CAAC;QACF,OAAO,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,iBAAiB,EAAE,CAAA;IACxE,CAAC,CAAC;SACD,IAAI,CAAC,UAAS,mBAAmB;QAC9B,oBAAoB,GAAG,mBAAmB,CAAA;QAC1C,OAAO,cAAc,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,YAAY,CAC1D,QAAQ,EACR,oBAAoB,CACvB,CAAA;IACL,CAAC,CAAC;SACD,IAAI,CAAC,UAAS,KAAK;QAChB,SAAS,GAAG,KAAK,CAAA;QACjB,OAAO,cAAc,CAAC,gBAAgB,CAClC,SAAS,CACZ,CAAC,sBAAsB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IAC5D,CAAC,CAAC;SACD,IAAI,CAAC,UAAS,UAAU;QACrB,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAA;QACzD,IAAI,OAAO,GAAG,IAAI,OAAO,CACrB,cAAc,CAAC,SAAS,EACxB,iBAAiB,CACpB,CAAA;QACD,OAAO,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QACnC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrB,CAAC,CAAC;SACD,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAA;AACjD,CAAC,CAAC,CAAA;AAEF,iBAAS,MAAM,CAAA"}
|
||||
@@ -1,93 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../api/BaseApi");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const Logger = require("../utils/Logger");
|
||||
const InjectionExtractor = require("../injection/InjectionExtractor");
|
||||
const IRegistryInfo_1 = require("../models/IRegistryInfo");
|
||||
const router = express.Router();
|
||||
router.get('/', function (req, res, next) {
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
let registries = [];
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.getAllRegistries();
|
||||
})
|
||||
.then(function (registriesAll) {
|
||||
registries = registriesAll;
|
||||
return registryHelper.getDefaultPushRegistryId();
|
||||
})
|
||||
.then(function (defaultPush) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'All registries retrieved');
|
||||
baseApi.data = {};
|
||||
baseApi.data.registries = registries;
|
||||
baseApi.data.defaultPushRegistryId = defaultPush;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/insert/', function (req, res, next) {
|
||||
let registryUser = req.body.registryUser + '';
|
||||
let registryPassword = req.body.registryPassword + '';
|
||||
let registryDomain = req.body.registryDomain + '';
|
||||
let registryImagePrefix = req.body.registryImagePrefix + '';
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.addRegistry(registryUser, registryPassword, registryDomain, registryImagePrefix, IRegistryInfo_1.IRegistryTypes.REMOTE_REG);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Registry is added.';
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// ERRORS if it's local
|
||||
router.post('/update/', function (req, res, next) {
|
||||
let registryId = req.body.id + '';
|
||||
let registryUser = req.body.registryUser + '';
|
||||
let registryPassword = req.body.registryPassword + '';
|
||||
let registryDomain = req.body.registryDomain + '';
|
||||
let registryImagePrefix = req.body.registryImagePrefix + '';
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.updateRegistry(registryId, registryUser, registryPassword, registryDomain, registryImagePrefix);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Registry is updated.';
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// ERRORS if default push is this OR if it's local
|
||||
router.post('/delete/', function (req, res, next) {
|
||||
let registryId = req.body.registryId + '';
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.deleteRegistry(registryId, false);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Registry deleted');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/setpush/', function (req, res, next) {
|
||||
let registryId = req.body.registryId + '';
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.setDefaultPushRegistry(registryId);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Push Registry changed');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=RegistriesRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"RegistriesRouter.js","sourceRoot":"","sources":["../../src/routes/RegistriesRouter.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,84 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../api/BaseApi");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const Injector = require("../injection/Injector");
|
||||
const SystemRouter = require("./system/SystemRouter");
|
||||
const AppsRouter = require("./apps/AppsRouter");
|
||||
const RegistriesRouter = require("./RegistriesRouter");
|
||||
const onFinished = require("on-finished");
|
||||
const InjectionExtractor = require("../injection/InjectionExtractor");
|
||||
const CaptainManager = require("../user/system/CaptainManager");
|
||||
const router = express.Router();
|
||||
const threadLockNamespace = {};
|
||||
router.use('/apps/webhooks/', Injector.injectUserForWebhook());
|
||||
router.use(Injector.injectUser());
|
||||
function isNotGetRequest(req) {
|
||||
return req.method !== 'GET';
|
||||
}
|
||||
router.use(function (req, res, next) {
|
||||
const user = InjectionExtractor.extractUserFromInjected(res).user;
|
||||
if (!user) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_NOT_AUTHORIZED, 'The request is not authorized.');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
if (!user.initialized) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_USER_NOT_INITIALIZED, 'User data is being loaded... Please wait...');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
const namespace = user.namespace;
|
||||
if (!namespace) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_NOT_AUTHORIZED, 'Cannot find the namespace attached to this user');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
const serviceManager = user.serviceManager;
|
||||
// All requests except GET might be making changes to some stuff that are not designed for an asynchronous process
|
||||
// I'm being extra cautious. But removal of this lock mechanism requires testing and consideration of edge cases.
|
||||
if (isNotGetRequest(req)) {
|
||||
if (threadLockNamespace[namespace]) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Another operation still in progress... please wait...');
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
let activeBuildAppName = serviceManager.isAnyBuildRunning();
|
||||
if (activeBuildAppName) {
|
||||
let response = new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, `An active build (${activeBuildAppName}) is in progress... please wait...`);
|
||||
res.send(response);
|
||||
return;
|
||||
}
|
||||
// we don't want the same space to go under two simultaneous changes
|
||||
threadLockNamespace[namespace] = true;
|
||||
onFinished(res, function () {
|
||||
threadLockNamespace[namespace] = false;
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
router.post('/changepassword/', function (req, res, next) {
|
||||
const namespace = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.namespace;
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
Promise.resolve() //
|
||||
.then(function (data) {
|
||||
return dataStore.getHashedPassword();
|
||||
})
|
||||
.then(function (savedHashedPassword) {
|
||||
return CaptainManager.getAuthenticator(namespace).changepass(req.body.oldPassword, req.body.newPassword, savedHashedPassword);
|
||||
})
|
||||
.then(function (hashedPassword) {
|
||||
return dataStore.setHashedPassword(hashedPassword);
|
||||
})
|
||||
.then(function () {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Password changed.'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.use('/apps/', AppsRouter);
|
||||
router.use('/registries/', RegistriesRouter);
|
||||
router.use('/system/', SystemRouter);
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=UserRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"UserRouter.js","sourceRoot":"","sources":["../../src/routes/UserRouter.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,80 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../../api/BaseApi");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const multer = require("multer");
|
||||
const InjectionExtractor = require("../../injection/InjectionExtractor");
|
||||
const TEMP_UPLOAD = 'temp_upload/';
|
||||
const router = express.Router();
|
||||
const upload = multer({
|
||||
dest: TEMP_UPLOAD,
|
||||
});
|
||||
router.get('/:appName/', function (req, res, next) {
|
||||
let appName = req.params.appName;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.getBuildStatus(appName);
|
||||
})
|
||||
.then(function (data) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'App build status retrieved');
|
||||
baseApi.data = data;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/:appName/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
let appName = req.params.appName;
|
||||
dataStore
|
||||
.getAppsDataStore()
|
||||
.getAppDefinition(appName)
|
||||
.then(function (app) {
|
||||
// nothing to do with app, just to make sure that it exists!
|
||||
next();
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/:appName/', upload.single('sourceFile'), function (req, res, next) {
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
const appName = req.params.appName;
|
||||
const isDetachedBuild = !!req.query.detached;
|
||||
const captainDefinitionContent = (req.body.captainDefinitionContent || '') + '';
|
||||
const gitHash = (req.body.gitHash || '') + '';
|
||||
let tarballSourceFilePath = !!req.file ? req.file.path : '';
|
||||
if (!!tarballSourceFilePath === !!captainDefinitionContent) {
|
||||
res.send(new BaseApi(ApiStatusCodes.ILLEGAL_OPERATION, 'Either tarballfile or captainDefinitionContent should be present.'));
|
||||
return;
|
||||
}
|
||||
Promise.resolve().then(function () {
|
||||
const promiseToDeployNewVer = serviceManager.deployNewVersion(appName, {
|
||||
uploadedTarPathSource: !!tarballSourceFilePath
|
||||
? {
|
||||
uploadedTarPath: tarballSourceFilePath,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
captainDefinitionContentSource: !!captainDefinitionContent
|
||||
? {
|
||||
captainDefinitionContent,
|
||||
gitHash,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
if (isDetachedBuild) {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK_DEPLOY_STARTED, 'Deploy is started'));
|
||||
}
|
||||
else {
|
||||
promiseToDeployNewVer
|
||||
.then(function () {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Deploy is done'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
}
|
||||
});
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=AppDataRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"AppDataRouter.js","sourceRoot":"","sources":["../../../src/routes/apps/AppDataRouter.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,287 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const fs = require("fs");
|
||||
const BaseApi = require("../../api/BaseApi");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const InjectionExtractor = require("../../injection/InjectionExtractor");
|
||||
const router = express.Router();
|
||||
const DEFAULT_APP_CAPTAIN_DEFINITION = JSON.stringify({
|
||||
schemaVersion: 2,
|
||||
dockerfileLines: [
|
||||
'FROM ' + CaptainConstants.appPlaceholderImageName,
|
||||
'CMD [ "npm", "start" ]',
|
||||
],
|
||||
});
|
||||
// Get a list of oneclickspps
|
||||
router.get('/oneclickapps', function (req, res, next) {
|
||||
fs.readdir(__dirname + '/../../dist/oneclick-apps', function (err, files) {
|
||||
if (err) {
|
||||
Logger.e(err);
|
||||
res.sendStatus(500);
|
||||
return;
|
||||
}
|
||||
let ret = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (files[i].endsWith('.js')) {
|
||||
ret.push(files[i].substring(0, files[i].length - 3));
|
||||
}
|
||||
}
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'One click app list is fetched.');
|
||||
baseApi.data = ret;
|
||||
res.send(baseApi);
|
||||
});
|
||||
});
|
||||
// unused iamges
|
||||
router.get('/unusedImages', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
let mostRecentLimit = Number(req.query.mostRecentLimit || '0');
|
||||
return serviceManager.getUnusedImages(mostRecentLimit);
|
||||
})
|
||||
.then(function (unusedImages) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Unused images retrieved.');
|
||||
baseApi.data = {};
|
||||
baseApi.data.unusedImages = unusedImages;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// unused iamges
|
||||
router.post('/deleteImages', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let imageIds = req.body.imageIds || [];
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.deleteImages(imageIds);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Images Deleted.');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// Get All App Definitions
|
||||
router.get('/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appsArray = [];
|
||||
dataStore
|
||||
.getAppsDataStore()
|
||||
.getAppDefinitions()
|
||||
.then(function (apps) {
|
||||
let promises = [];
|
||||
Object.keys(apps).forEach(function (key, index) {
|
||||
let app = apps[key];
|
||||
app.appName = key;
|
||||
app.isAppBuilding = serviceManager.isAppBuilding(key);
|
||||
app.appPushWebhook = app.appPushWebhook || undefined;
|
||||
appsArray.push(app);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore.getDefaultAppNginxConfig();
|
||||
})
|
||||
.then(function (defaultNginxConfig) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'App definitions are retrieved.');
|
||||
baseApi.data = {
|
||||
appDefinitions: appsArray,
|
||||
rootDomain: dataStore.getRootDomain(),
|
||||
defaultNginxConfig: defaultNginxConfig,
|
||||
};
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/enablebasedomainssl/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
const appName = req.body.appName;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.enableSslForApp(appName);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'General SSL is enabled for: ' + appName;
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/customdomain/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
let customDomain = (req.body.customDomain || '').toLowerCase();
|
||||
// verify customdomain.com going through the default NGINX
|
||||
// Add customdomain.com to app in Data Store
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.addCustomDomain(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Custom domain is enabled for: ' +
|
||||
appName +
|
||||
' at ' +
|
||||
customDomain;
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/removecustomdomain/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
let customDomain = (req.body.customDomain || '').toLowerCase();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.removeCustomDomain(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Custom domain is removed for: ' +
|
||||
appName +
|
||||
' at ' +
|
||||
customDomain;
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/enablecustomdomainssl/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
let customDomain = (req.body.customDomain || '').toLowerCase();
|
||||
// Check if customdomain is already associated with app. If not, error out.
|
||||
// Verify customdomain.com is served from /customdomain.com/
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.enableCustomDomainSsl(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = `Custom domain SSL is enabled for: ${appName} at ${customDomain} `;
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/register/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
let hasPersistentData = !!req.body.hasPersistentData;
|
||||
let appCreated = false;
|
||||
Logger.d('Registering app started: ' + appName);
|
||||
dataStore
|
||||
.getAppsDataStore()
|
||||
.registerAppDefinition(appName, hasPersistentData)
|
||||
.then(function () {
|
||||
appCreated = true;
|
||||
})
|
||||
.then(function () {
|
||||
return serviceManager.deployNewVersion(appName, {
|
||||
captainDefinitionContentSource: {
|
||||
captainDefinitionContent: DEFAULT_APP_CAPTAIN_DEFINITION,
|
||||
gitHash: '',
|
||||
},
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('AppName is saved: ' + appName);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'App Definition Saved'));
|
||||
})
|
||||
.catch(function (error) {
|
||||
function createRejectionPromise() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
if (appCreated) {
|
||||
return dataStore
|
||||
.getAppsDataStore()
|
||||
.deleteAppDefinition(appName)
|
||||
.then(function () {
|
||||
return createRejectionPromise();
|
||||
});
|
||||
}
|
||||
else {
|
||||
return createRejectionPromise();
|
||||
}
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/delete/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
Logger.d('Deleting app started: ' + appName);
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
return serviceManager.removeApp(appName);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('AppName is deleted: ' + appName);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'App is deleted'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/update/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
const serviceManager = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.serviceManager;
|
||||
let appName = req.body.appName;
|
||||
let nodeId = req.body.nodeId;
|
||||
let notExposeAsWebApp = req.body.notExposeAsWebApp;
|
||||
let customNginxConfig = req.body.customNginxConfig;
|
||||
let forceSsl = !!req.body.forceSsl;
|
||||
let repoInfo = req.body.repoInfo || {};
|
||||
let envVars = req.body.envVars || [];
|
||||
let volumes = req.body.volumes || [];
|
||||
let ports = req.body.ports || [];
|
||||
let instanceCount = req.body.instanceCount || '0';
|
||||
let preDeployFunction = req.body.preDeployFunction || '';
|
||||
if (repoInfo.user) {
|
||||
repoInfo.user = repoInfo.user.trim();
|
||||
}
|
||||
if (repoInfo.repo) {
|
||||
repoInfo.repo = repoInfo.repo.trim();
|
||||
}
|
||||
if (repoInfo.branch) {
|
||||
repoInfo.branch = repoInfo.branch.trim();
|
||||
}
|
||||
Logger.d('Updating app started: ' + appName);
|
||||
serviceManager
|
||||
.updateAppDefinition(appName, Number(instanceCount), envVars, volumes, nodeId, notExposeAsWebApp, forceSsl, ports, repoInfo, customNginxConfig, preDeployFunction)
|
||||
.then(function () {
|
||||
Logger.d('AppName is updated: ' + appName);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Updated App Definition Saved'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=AppDefinitionRouter.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
"use strict";
|
||||
const WebhooksRouter = require("./WebhooksRouter");
|
||||
const AppDefinitionRouter = require("./AppDefinitionRouter");
|
||||
const AppDataRouter = require("./AppDataRouter");
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
router.use('/appDefinitions/', AppDefinitionRouter);
|
||||
router.use('/appData/', AppDataRouter);
|
||||
// semi-secured end points:
|
||||
router.use('/webhooks/', WebhooksRouter);
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=AppsRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"AppsRouter.js","sourceRoot":"","sources":["../../../src/routes/apps/AppsRouter.ts"],"names":[],"mappings":";AAAA,mDAAmD;AACnD,6DAA6D;AAC7D,iDAAiD;AAEjD,mCAAmC;AAEnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAA;AAE/B,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAA;AAEnD,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA;AAEtC,2BAA2B;AAC3B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;AAExC,iBAAS,MAAM,CAAA"}
|
||||
@@ -1,78 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const InjectionExtractor = require("../../injection/InjectionExtractor");
|
||||
const router = express.Router();
|
||||
const urlencodedParser = bodyParser.urlencoded({
|
||||
extended: true,
|
||||
});
|
||||
function getPushedBranches(req) {
|
||||
const pushedBranches = [];
|
||||
// find which branch is pushed
|
||||
// Add it in pushedBranches
|
||||
let isGithub = req.header('X-GitHub-Event') === 'push';
|
||||
let isBitbucket = req.header('X-Event-Key') === 'repo:push' &&
|
||||
req.header('X-Request-UUID') &&
|
||||
req.header('X-Hook-UUID');
|
||||
let isGitlab = req.header('X-Gitlab-Event') === 'Push Hook';
|
||||
if (isGithub) {
|
||||
let refPayloadByFormEncoded = req.body.payload;
|
||||
let bodyJson = req.body;
|
||||
if (refPayloadByFormEncoded) {
|
||||
bodyJson = JSON.parse(refPayloadByFormEncoded);
|
||||
}
|
||||
let ref = bodyJson.ref; // "refs/heads/somebranch"
|
||||
pushedBranches.push(ref.substring(11, ref.length));
|
||||
}
|
||||
else if (isBitbucket) {
|
||||
for (let i = 0; i < req.body.push.changes.length; i++) {
|
||||
pushedBranches.push(req.body.push.changes[i].new.name);
|
||||
}
|
||||
}
|
||||
else if (isGitlab) {
|
||||
let ref = req.body.ref; // "refs/heads/somebranch"
|
||||
pushedBranches.push(ref.substring(11, ref.length));
|
||||
}
|
||||
return pushedBranches;
|
||||
}
|
||||
router.post('/triggerbuild', urlencodedParser, function (req, res, next) {
|
||||
// From this point on, we don't want to error out. Just do the build process in the background
|
||||
res.sendStatus(200);
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
const extracted = InjectionExtractor.extractAppAndUserForWebhook(res);
|
||||
let serviceManager = extracted.user.serviceManager;
|
||||
let namespace = extracted.user.namespace;
|
||||
let appName = extracted.appName;
|
||||
let app = extracted.app;
|
||||
if (!app || !serviceManager || !namespace || !appName) {
|
||||
throw new Error('Something went wrong during trigger build. Cannot extract app information from the payload.');
|
||||
}
|
||||
const repoInfo = app.appPushWebhook.repoInfo;
|
||||
// if we didn't detect branches, the POST might have come from another source that we don't
|
||||
// explicitly support. Therefore, we just let it go through and triggers a build regardless.
|
||||
const pushedBranches = getPushedBranches(req);
|
||||
if (pushedBranches.length > 0) {
|
||||
let branchIsTracked = false;
|
||||
for (let i = 0; i < pushedBranches.length; i++) {
|
||||
if (pushedBranches[i] === repoInfo.branch) {
|
||||
branchIsTracked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// POST call was triggered due to another branch being pushed. We don't need to trigger the build.
|
||||
if (!branchIsTracked) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return serviceManager.deployNewVersion(appName, {
|
||||
repoInfoSource: repoInfo,
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
Logger.e(error);
|
||||
});
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=WebhooksRouter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"WebhooksRouter.js","sourceRoot":"","sources":["../../../src/routes/apps/WebhooksRouter.ts"],"names":[],"mappings":";AAAA,mCAAmC;AACnC,0CAA0C;AAI1C,6CAA6C;AAG7C,yEAAyE;AAEzE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAA;AAE/B,MAAM,gBAAgB,GAAG,UAAU,CAAC,UAAU,CAAC;IAC3C,QAAQ,EAAE,IAAI;CACjB,CAAC,CAAA;AAEF,SAAS,iBAAiB,CAAC,GAAoB;IAC3C,MAAM,cAAc,GAAa,EAAE,CAAA;IAEnC,8BAA8B;IAC9B,2BAA2B;IAC3B,IAAI,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,MAAM,CAAA;IACtD,IAAI,WAAW,GACX,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,WAAW;QACzC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC5B,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAC7B,IAAI,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,WAAW,CAAA;IAE3D,IAAI,QAAQ,EAAE;QACV,IAAI,uBAAuB,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAA;QAC9C,IAAI,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAA;QACvB,IAAI,uBAAuB,EAAE;YACzB,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;SACjD;QACD,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAA,CAAC,0BAA0B;QACjD,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;KACrD;SAAM,IAAI,WAAW,EAAE;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACnD,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;SACzD;KACJ;SAAM,IAAI,QAAQ,EAAE;QACjB,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAA,CAAC,0BAA0B;QACjD,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;KACrD;IACD,OAAO,cAAc,CAAA;AACzB,CAAC;AAED,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,gBAAgB,EAAE,UAAS,GAAG,EAAE,GAAG,EAAE,IAAI;IAClE,8FAA8F;IAC9F,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IAEnB,OAAO,CAAC,OAAO,EAAE;SACZ,IAAI,CAAC;QACF,MAAM,SAAS,GAAG,kBAAkB,CAAC,2BAA2B,CAC5D,GAAG,CACN,CAAA;QACD,IAAI,cAAc,GAAG,SAAS,CAAC,IAAI,CAAC,cAAc,CAAA;QAClD,IAAI,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAA;QACxC,IAAI,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;QAC/B,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,CAAA;QAEvB,IAAI,CAAC,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE;YACnD,MAAM,IAAI,KAAK,CACX,6FAA6F,CAChG,CAAA;SACJ;QAED,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAe,CAAC,QAAQ,CAAA;QAC7C,2FAA2F;QAC3F,4FAA4F;QAE5F,MAAM,cAAc,GAAa,iBAAiB,CAAC,GAAG,CAAC,CAAA;QACvD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE;YAC3B,IAAI,eAAe,GAAG,KAAK,CAAA;YAE3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC5C,IAAI,cAAc,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,MAAM,EAAE;oBACvC,eAAe,GAAG,IAAI,CAAA;oBACtB,MAAK;iBACR;aACJ;YAED,kGAAkG;YAClG,IAAI,CAAC,eAAe,EAAE;gBAClB,OAAM;aACT;SACJ;QAED,OAAO,cAAc,CAAC,gBAAgB,CAAC,OAAO,EAAE;YAC5C,cAAc,EAAE,QAAQ;SAC3B,CAAC,CAAA;IACN,CAAC,CAAC;SACD,KAAK,CAAC,UAAS,KAAK;QACjB,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC,CAAC,CAAA;AACV,CAAC,CAAC,CAAA;AAEF,iBAAS,MAAM,CAAA"}
|
||||
@@ -1,80 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../../api/BaseApi");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const CaptainManager = require("../../user/system/CaptainManager");
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const InjectionExtractor = require("../../injection/InjectionExtractor");
|
||||
const uuid = require("uuid/v4");
|
||||
const IRegistryInfo_1 = require("../../models/IRegistryInfo");
|
||||
const router = express.Router();
|
||||
// ERRORS if a local already exists in DB
|
||||
router.post('/enableregistry/', function (req, res, next) {
|
||||
const captainManager = CaptainManager.get();
|
||||
const password = uuid();
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get()
|
||||
.getDockerRegistry()
|
||||
.enableRegistrySsl();
|
||||
})
|
||||
.then(function () {
|
||||
return captainManager
|
||||
.getDockerRegistry()
|
||||
.ensureDockerRegistryRunningOnThisNode(password);
|
||||
})
|
||||
.then(function () {
|
||||
return registryHelper.getAllRegistries();
|
||||
})
|
||||
.then(function (allRegs) {
|
||||
for (let index = 0; index < allRegs.length; index++) {
|
||||
const element = allRegs[index];
|
||||
if (element.registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'There is already a local registry set up!');
|
||||
}
|
||||
}
|
||||
let user = CaptainConstants.captainRegistryUsername;
|
||||
let domain = captainManager
|
||||
.getDockerRegistry()
|
||||
.getLocalRegistryDomainAndPort();
|
||||
return registryHelper.addRegistry(user, password, domain, user, IRegistryInfo_1.IRegistryTypes.LOCAL_REG);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Local registry is created.';
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
// ERRORS if default push is this
|
||||
router.post('/disableregistry/', function (req, res, next) {
|
||||
const captainManager = CaptainManager.get();
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.getAllRegistries();
|
||||
})
|
||||
.then(function (regs) {
|
||||
let localRegistryId = '';
|
||||
for (let idx = 0; idx < regs.length; idx++) {
|
||||
const element = regs[idx];
|
||||
if (element.registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG) {
|
||||
localRegistryId = element.id;
|
||||
}
|
||||
}
|
||||
return registryHelper.deleteRegistry(localRegistryId, true);
|
||||
})
|
||||
.then(function () {
|
||||
return captainManager.getDockerRegistry().ensureServiceRemoved();
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Local registry is removed.';
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=SystemRouteSelfHostRegistry.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SystemRouteSelfHostRegistry.js","sourceRoot":"","sources":["../../../src/routes/system/SystemRouteSelfHostRegistry.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,267 +0,0 @@
|
||||
"use strict";
|
||||
const express = require("express");
|
||||
const BaseApi = require("../../api/BaseApi");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const CaptainManager = require("../../user/system/CaptainManager");
|
||||
const Validator = require("validator");
|
||||
const SystemRouteSelfHostRegistry = require("./SystemRouteSelfHostRegistry");
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const InjectionExtractor = require("../../injection/InjectionExtractor");
|
||||
const Utils_1 = require("../../utils/Utils");
|
||||
const router = express.Router();
|
||||
router.use('/selfhostregistry/', SystemRouteSelfHostRegistry);
|
||||
router.post('/changerootdomain/', function (req, res, next) {
|
||||
let requestedCustomDomain = Utils_1.default.removeHttpHttps((req.body.rootDomain || '').toLowerCase());
|
||||
if (!requestedCustomDomain ||
|
||||
requestedCustomDomain.length < 3 ||
|
||||
requestedCustomDomain.indexOf('/') >= 0 ||
|
||||
requestedCustomDomain.indexOf(':') >= 0 ||
|
||||
requestedCustomDomain.indexOf('%') >= 0 ||
|
||||
requestedCustomDomain.indexOf(' ') >= 0 ||
|
||||
requestedCustomDomain.indexOf('\\') >= 0) {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Bad domain name.'));
|
||||
return;
|
||||
}
|
||||
CaptainManager.get()
|
||||
.changeCaptainRootDomain(requestedCustomDomain)
|
||||
.then(function () {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Root domain changed.'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/enablessl/', function (req, res, next) {
|
||||
const emailAddress = req.body.emailAddress || '';
|
||||
if (!emailAddress ||
|
||||
emailAddress.length < 3 ||
|
||||
emailAddress.indexOf('/') >= 0 ||
|
||||
emailAddress.indexOf(':') >= 0 ||
|
||||
emailAddress.indexOf('%') >= 0 ||
|
||||
emailAddress.indexOf(' ') >= 0 ||
|
||||
emailAddress.indexOf('\\') >= 0 ||
|
||||
!Validator.isEmail(emailAddress)) {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Bad email address.'));
|
||||
return;
|
||||
}
|
||||
CaptainManager.get()
|
||||
.enableSsl(emailAddress)
|
||||
.then(function () {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Root SSL Enabled.'));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/forcessl/', function (req, res, next) {
|
||||
let isEnabled = !!req.body.isEnabled;
|
||||
CaptainManager.get()
|
||||
.forceSsl(isEnabled)
|
||||
.then(function () {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'Non-SSL traffic is now ' +
|
||||
(isEnabled ? 'rejected.' : 'allowed.')));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/info/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dataStore.getHasRootSsl();
|
||||
})
|
||||
.then(function (hasRootSsl) {
|
||||
return {
|
||||
hasRootSsl: hasRootSsl,
|
||||
forceSsl: CaptainManager.get().getForceSslValue(),
|
||||
rootDomain: dataStore.hasCustomDomain()
|
||||
? dataStore.getRootDomain()
|
||||
: '',
|
||||
};
|
||||
})
|
||||
.then(function (data) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Captain info retrieved');
|
||||
baseApi.data = data;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/loadbalancerinfo/', function (req, res, next) {
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get()
|
||||
.getLoadBalanceManager()
|
||||
.getInfo();
|
||||
})
|
||||
.then(function (data) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Load Balancer info retrieved');
|
||||
baseApi.data = data;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/versionInfo/', function (req, res, next) {
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().getCaptainImageTags();
|
||||
})
|
||||
.then(function (tagList) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Version Info Retrieved');
|
||||
let currentVersion = CaptainConstants.version.split('.');
|
||||
let latestVersion = CaptainConstants.version.split('.');
|
||||
let canUpdate = false;
|
||||
for (let i = 0; i < tagList.length; i++) {
|
||||
let tag = tagList[i].split('.');
|
||||
if (tag.length !== 3) {
|
||||
continue;
|
||||
}
|
||||
if (Number(tag[0]) > Number(currentVersion[0])) {
|
||||
canUpdate = true;
|
||||
latestVersion = tag;
|
||||
break;
|
||||
}
|
||||
else if (Number(tag[0]) === Number(currentVersion[0]) &&
|
||||
Number(tag[1]) > Number(currentVersion[1])) {
|
||||
canUpdate = true;
|
||||
latestVersion = tag;
|
||||
break;
|
||||
}
|
||||
else if (Number(tag[0]) === Number(currentVersion[0]) &&
|
||||
Number(tag[1]) === Number(currentVersion[1]) &&
|
||||
Number(tag[2]) > Number(currentVersion[2])) {
|
||||
canUpdate = true;
|
||||
latestVersion = tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
baseApi.data = {
|
||||
currentVersion: currentVersion.join('.'),
|
||||
latestVersion: latestVersion.join('.'),
|
||||
canUpdate: canUpdate,
|
||||
};
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/versionInfo/', function (req, res, next) {
|
||||
let latestVersion = req.body.latestVersion;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().updateCaptain(latestVersion);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Captain update process has started...');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/netdata/', function (req, res, next) {
|
||||
const dataStore = InjectionExtractor.extractUserFromInjected(res).user
|
||||
.dataStore;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dataStore.getNetDataInfo();
|
||||
})
|
||||
.then(function (data) {
|
||||
data.netDataUrl =
|
||||
CaptainConstants.captainSubDomain +
|
||||
'.' +
|
||||
dataStore.getRootDomain() +
|
||||
CaptainConstants.netDataRelativePath;
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Netdata info retrieved');
|
||||
baseApi.data = data;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/netdata/', function (req, res, next) {
|
||||
let netDataInfo = req.body.netDataInfo;
|
||||
netDataInfo.netDataUrl = undefined; // Frontend app returns this value, but we really don't wanna save this.
|
||||
// root address is subject to change.
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().updateNetDataInfo(netDataInfo);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Netdata info is updated');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/nginxconfig/', function (req, res, next) {
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().getNginxConfig();
|
||||
})
|
||||
.then(function (data) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Nginx config retrieved');
|
||||
baseApi.data = data;
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/nginxconfig/', function (req, res, next) {
|
||||
let baseConfigCustomValue = req.body.baseConfig.customValue;
|
||||
let captainConfigCustomValue = req.body.captainConfig.customValue;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().setNginxConfig(baseConfigCustomValue, captainConfigCustomValue);
|
||||
})
|
||||
.then(function () {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Nginx config is updated');
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.get('/nodes/', function (req, res, next) {
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return CaptainManager.get().getNodesInfo();
|
||||
})
|
||||
.then(function (data) {
|
||||
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, 'Node info retrieved');
|
||||
baseApi.data = { nodes: data };
|
||||
res.send(baseApi);
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
router.post('/nodes/', function (req, res, next) {
|
||||
const MANAGER = 'manager';
|
||||
const WORKER = 'worker';
|
||||
const registryHelper = InjectionExtractor.extractUserFromInjected(res).user.serviceManager.getRegistryHelper();
|
||||
let isManager;
|
||||
if (req.body.nodeType === MANAGER) {
|
||||
isManager = true;
|
||||
}
|
||||
else if (req.body.nodeType === WORKER) {
|
||||
isManager = false;
|
||||
}
|
||||
else {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Node type should be either manager or worker'));
|
||||
return;
|
||||
}
|
||||
let privateKey = req.body.privateKey;
|
||||
let remoteNodeIpAddress = req.body.remoteNodeIpAddress;
|
||||
let captainIpAddress = req.body.captainIpAddress;
|
||||
if (!captainIpAddress || !remoteNodeIpAddress || !privateKey) {
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Private Key, Captain IP address, remote IP address and remote username should all be present'));
|
||||
return;
|
||||
}
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return registryHelper.getDefaultPushRegistryId();
|
||||
})
|
||||
.then(function (defaultRegistry) {
|
||||
if (!defaultRegistry) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'There is no default Docker Registry. You need a repository for your images before adding nodes. Read docs.');
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return CaptainManager.get().joinDockerNode(captainIpAddress, isManager, remoteNodeIpAddress, privateKey);
|
||||
})
|
||||
.then(function () {
|
||||
let msg = 'Docker node is successfully joined.';
|
||||
Logger.d(msg);
|
||||
res.send(new BaseApi(ApiStatusCodes.STATUS_OK, msg));
|
||||
})
|
||||
.catch(ApiStatusCodes.createCatcher(res));
|
||||
});
|
||||
module.exports = router;
|
||||
//# sourceMappingURL=SystemRouter.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,138 +0,0 @@
|
||||
"use strict";
|
||||
const jwt = require("jsonwebtoken");
|
||||
const uuid = require("uuid/v4");
|
||||
const bcrypt = require("bcryptjs");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const EnvVar = require("../utils/EnvVars");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const Logger = require("../utils/Logger");
|
||||
const captainDefaultPassword = EnvVar.DEFAULT_PASSWORD || 'captain42';
|
||||
const COOKIE_AUTH_SUFFIX = 'cookie-';
|
||||
const WEBHOOK_APP_PUSH_SUFFIX = '-webhook-app-push';
|
||||
class Authenticator {
|
||||
constructor(secret, namespace) {
|
||||
this.encryptionKey = secret + namespace; // making encryption key unique per namespace!
|
||||
this.namespace = namespace;
|
||||
this.tokenVersion = CaptainConstants.isDebug ? 'test' : uuid();
|
||||
}
|
||||
changepass(oldPass, newPass, savedHashedPassword) {
|
||||
const self = this;
|
||||
oldPass = oldPass || '';
|
||||
newPass = newPass || '';
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
if (!oldPass || !newPass || newPass.length < 8) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Password is too small.');
|
||||
}
|
||||
return self.isPasswordCorrect(oldPass, savedHashedPassword);
|
||||
})
|
||||
.then(function (isPasswordCorrect) {
|
||||
if (!isPasswordCorrect) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_WRONG_PASSWORD, 'Old password is incorrect.');
|
||||
}
|
||||
self.tokenVersion = uuid();
|
||||
const hashed = bcrypt.hashSync(self.encryptionKey + newPass, bcrypt.genSaltSync(10));
|
||||
return hashed;
|
||||
});
|
||||
}
|
||||
isPasswordCorrect(password, savedHashedPassword) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
password = password || '';
|
||||
if (!savedHashedPassword) {
|
||||
return captainDefaultPassword === password;
|
||||
}
|
||||
return bcrypt.compareSync(self.encryptionKey + password, savedHashedPassword);
|
||||
});
|
||||
}
|
||||
getAuthTokenForCookies(password, savedHashedPassword) {
|
||||
return this.getAuthToken(password, savedHashedPassword, COOKIE_AUTH_SUFFIX);
|
||||
}
|
||||
getAuthToken(password, savedHashedPassword, keySuffix) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.isPasswordCorrect(password, savedHashedPassword);
|
||||
})
|
||||
.then(function (isPasswordCorrect) {
|
||||
if (!isPasswordCorrect) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_WRONG_PASSWORD, 'Password is incorrect.');
|
||||
}
|
||||
const userObj = {
|
||||
namespace: self.namespace,
|
||||
tokenVersion: self.tokenVersion,
|
||||
};
|
||||
return jwt.sign({
|
||||
data: userObj,
|
||||
}, self.encryptionKey + (keySuffix ? keySuffix : ''), { expiresIn: '10000h' });
|
||||
});
|
||||
}
|
||||
decodeAuthTokenFromCookies(token) {
|
||||
return this.decodeAuthToken(token, COOKIE_AUTH_SUFFIX);
|
||||
}
|
||||
decodeAuthToken(token, keySuffix) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
jwt.verify(token, self.encryptionKey + (keySuffix ? keySuffix : ''), function (err, rawDecoded) {
|
||||
if (err) {
|
||||
Logger.e(err);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'Auth token corrupted'));
|
||||
return;
|
||||
}
|
||||
const decodedData = rawDecoded.data;
|
||||
if (decodedData.tokenVersion !== self.tokenVersion) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'Auth token is no longer valid. Request for a new auth token'));
|
||||
return;
|
||||
}
|
||||
if (decodedData.namespace !== self.namespace) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'Auth token does not match the namespace'));
|
||||
return;
|
||||
}
|
||||
resolve(decodedData);
|
||||
});
|
||||
});
|
||||
}
|
||||
getAppPushWebhookToken(appName, tokenVersion) {
|
||||
const self = this;
|
||||
if (!appName) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'App name are required for webhook token..');
|
||||
}
|
||||
return self.getGenericToken({
|
||||
tokenVersion: tokenVersion,
|
||||
appName: appName,
|
||||
}, WEBHOOK_APP_PUSH_SUFFIX);
|
||||
}
|
||||
decodeAppPushWebhookToken(token) {
|
||||
const self = this;
|
||||
return self.decodeGenericToken(token, WEBHOOK_APP_PUSH_SUFFIX);
|
||||
}
|
||||
getGenericToken(obj, keySuffix) {
|
||||
const self = this;
|
||||
obj.namespace = self.namespace;
|
||||
return Promise.resolve().then(function () {
|
||||
return jwt.sign({
|
||||
data: obj,
|
||||
}, self.encryptionKey + (keySuffix ? keySuffix : ''));
|
||||
});
|
||||
}
|
||||
decodeGenericToken(token, keySuffix) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
jwt.verify(token, self.encryptionKey + (keySuffix ? keySuffix : ''), function (err, rawDecoded) {
|
||||
if (err) {
|
||||
Logger.e(err);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'Token corrupted'));
|
||||
return;
|
||||
}
|
||||
const decodedData = rawDecoded.data;
|
||||
if (decodedData.namespace !== self.namespace) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_AUTH_TOKEN_INVALID, 'token does not match the namespace'));
|
||||
return;
|
||||
}
|
||||
resolve(decodedData);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = Authenticator;
|
||||
//# sourceMappingURL=Authenticator.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Authenticator.js","sourceRoot":"","sources":["../../src/user/Authenticator.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,39 +0,0 @@
|
||||
"use strict";
|
||||
const Logger = require("../utils/Logger");
|
||||
class BuildLog {
|
||||
constructor(size) {
|
||||
this.size = size;
|
||||
this.clear();
|
||||
}
|
||||
onBuildFailed(error) {
|
||||
this.log('----------------------');
|
||||
this.log('Deploy failed!');
|
||||
this.log(error);
|
||||
this.isBuildFailed = true;
|
||||
}
|
||||
clear() {
|
||||
this.isBuildFailed = false;
|
||||
this.firstLineNumber = -this.size;
|
||||
this.lines = [];
|
||||
for (let i = 0; i < this.size; i++) {
|
||||
this.lines.push('');
|
||||
}
|
||||
}
|
||||
log(msg) {
|
||||
msg = (msg || '') + '';
|
||||
this.lines.shift();
|
||||
this.lines.push(msg);
|
||||
this.firstLineNumber++;
|
||||
Logger.dev(msg);
|
||||
}
|
||||
getLogs() {
|
||||
const self = this;
|
||||
// if we don't copy the object, "lines" can get changed but firstLineNumber stay as is, causing bug!
|
||||
return JSON.parse(JSON.stringify({
|
||||
lines: self.lines,
|
||||
firstLineNumber: self.firstLineNumber,
|
||||
}));
|
||||
}
|
||||
}
|
||||
module.exports = BuildLog;
|
||||
//# sourceMappingURL=BuildLog.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"BuildLog.js","sourceRoot":"","sources":["../../src/user/BuildLog.ts"],"names":[],"mappings":";AAAA,0CAA0C;AAE1C,MAAM,QAAQ;IAKV,YAAoB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;QAC5B,IAAI,CAAC,KAAK,EAAE,CAAA;IAChB,CAAC;IAED,aAAa,CAAC,KAAa;QACvB,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;QAClC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;QAC1B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACf,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;IAC7B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,aAAa,GAAG,KAAK,CAAA;QAC1B,IAAI,CAAC,eAAe,GAAG,CAAC,IAAI,CAAC,IAAI,CAAA;QACjC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;SACtB;IACL,CAAC;IAED,GAAG,CAAC,GAAW;QACX,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACpB,IAAI,CAAC,eAAe,EAAE,CAAA;QACtB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACnB,CAAC;IAED,OAAO;QACH,MAAM,IAAI,GAAG,IAAI,CAAA;QACjB,oGAAoG;QACpG,OAAO,IAAI,CAAC,KAAK,CACb,IAAI,CAAC,SAAS,CAAC;YACX,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,eAAe,EAAE,IAAI,CAAC,eAAe;SACxC,CAAC,CACL,CAAA;IACL,CAAC;CACJ;AAED,iBAAS,QAAQ,CAAA"}
|
||||
@@ -1,180 +0,0 @@
|
||||
"use strict";
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const Logger = require("../utils/Logger");
|
||||
const IRegistryInfo_1 = require("../models/IRegistryInfo");
|
||||
const Utils_1 = require("../utils/Utils");
|
||||
class DockerRegistryHelper {
|
||||
constructor(dataStore, dockerApi) {
|
||||
this.dockerApi = dockerApi;
|
||||
this.registriesDataStore = dataStore.getRegistriesDataStore();
|
||||
}
|
||||
retagAndPushIfDefaultPushExist(imageName, version, buildLogs) {
|
||||
const self = this;
|
||||
let allRegistries;
|
||||
let fullImageName = imageName + ':' + version;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
if (!imageName)
|
||||
throw new Error('no image name! cannot re-tag!');
|
||||
if (imageName.indexOf('/') >= 0 || imageName.indexOf(':') >= 0)
|
||||
throw new Error('ImageName should not contain "/" or ":" before re-tagging!');
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (data) {
|
||||
allRegistries = data;
|
||||
return self.getDefaultPushRegistryId();
|
||||
})
|
||||
.then(function (defaultRegId) {
|
||||
let ret = undefined;
|
||||
for (let idx = 0; idx < allRegistries.length; idx++) {
|
||||
const element = allRegistries[idx];
|
||||
if (defaultRegId && element.id === defaultRegId) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data)
|
||||
return fullImageName;
|
||||
fullImageName =
|
||||
data.registryDomain +
|
||||
'/' +
|
||||
data.registryImagePrefix +
|
||||
'/' +
|
||||
fullImageName;
|
||||
return self
|
||||
.getDockerAuthObjectForImageName(fullImageName)
|
||||
.then(function (authObj) {
|
||||
if (!authObj) {
|
||||
throw new Error('Docker Auth Object is NULL just after re-tagging! Something is wrong!');
|
||||
}
|
||||
Logger.d('Docker Auth is found. Pushing the image...');
|
||||
return self.dockerApi
|
||||
.pushImage(fullImageName, authObj, buildLogs)
|
||||
.catch(function (error) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
Logger.e('PUSH FAILED');
|
||||
Logger.e(error);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Push failed: ' + error));
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return fullImageName;
|
||||
});
|
||||
});
|
||||
}
|
||||
getDockerAuthObjectForImageName(imageName) {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
//
|
||||
return self.getAllRegistries();
|
||||
})
|
||||
.then(function (regs) {
|
||||
for (let index = 0; index < regs.length; index++) {
|
||||
const element = regs[index];
|
||||
const prefix = element.registryImagePrefix;
|
||||
const registryIdentifierPrefix = element.registryDomain +
|
||||
(prefix ? '/' + prefix : '') +
|
||||
'/';
|
||||
if (imageName.startsWith(registryIdentifierPrefix)) {
|
||||
return {
|
||||
serveraddress: element.registryDomain,
|
||||
username: element.registryUser,
|
||||
password: element.registryPassword,
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
setDefaultPushRegistry(registryId) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.registriesDataStore.setDefaultPushRegistryId(registryId);
|
||||
});
|
||||
}
|
||||
getDefaultPushRegistryId() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.registriesDataStore.getDefaultPushRegistryId();
|
||||
});
|
||||
}
|
||||
deleteRegistry(registryId, allowLocalDelete) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.getDefaultPushRegistryId();
|
||||
})
|
||||
.then(function (registryIdDefaultPush) {
|
||||
if (registryId === registryIdDefaultPush) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'Cannot remove the default push. First change the default push.');
|
||||
}
|
||||
return self.registriesDataStore.getRegistryById(registryId);
|
||||
})
|
||||
.then(function (registry) {
|
||||
if (registry.registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG &&
|
||||
!allowLocalDelete) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'You cannot delete self-hosted registry.');
|
||||
}
|
||||
return self.registriesDataStore.deleteRegistry(registryId);
|
||||
});
|
||||
}
|
||||
getAllRegistries() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.registriesDataStore.getAllRegistries();
|
||||
});
|
||||
}
|
||||
addRegistry(registryUser, registryPassword, registryDomain, registryImagePrefix, registryType) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
registryDomain = Utils_1.default.removeHttpHttps(registryDomain);
|
||||
if (registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG) {
|
||||
// We don't check the auth details for local registry. We create it, we know it's correct!
|
||||
return;
|
||||
}
|
||||
return self.dockerApi
|
||||
.checkRegistryAuth({
|
||||
username: registryUser,
|
||||
password: registryPassword,
|
||||
serveraddress: registryDomain,
|
||||
})
|
||||
.catch(function (err) {
|
||||
Logger.e(err);
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.AUTHENTICATION_FAILED, 'Authentication failed. Either username, password or domain is incorrect.');
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return self.registriesDataStore.getAllRegistries();
|
||||
})
|
||||
.then(function (allRegs) {
|
||||
let promiseToAddRegistry = self.registriesDataStore.addRegistryToDb(registryUser, registryPassword, registryDomain, registryImagePrefix, registryType);
|
||||
// Product decision. We want to make the first added registry the default one,
|
||||
// this way, it's easier for new users to grasp the concept of default push registry.
|
||||
if (allRegs.length === 0) {
|
||||
promiseToAddRegistry = promiseToAddRegistry //
|
||||
.then(function (idOfNewReg) {
|
||||
return self.registriesDataStore
|
||||
.setDefaultPushRegistryId(idOfNewReg)
|
||||
.then(function () {
|
||||
return idOfNewReg;
|
||||
});
|
||||
});
|
||||
}
|
||||
return promiseToAddRegistry;
|
||||
});
|
||||
}
|
||||
updateRegistry(id, registryUser, registryPassword, registryDomain, registryImagePrefix) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
registryDomain = Utils_1.default.removeHttpHttps(registryDomain);
|
||||
return self.registriesDataStore.updateRegistry(id, registryUser, registryPassword, registryDomain, registryImagePrefix);
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = DockerRegistryHelper;
|
||||
//# sourceMappingURL=DockerRegistryHelper.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,346 +0,0 @@
|
||||
"use strict";
|
||||
/*
|
||||
+---------------------------------+
|
||||
+------------------+ | |
|
||||
|------------------| | Assign the final Image | +--------------------+
|
||||
|| || | (library/mysql +----------------+ Retag and push +<-----------+
|
||||
|| Update |-----------+ or | | IF NEEDED | |
|
||||
||Captain Service || | repo.com:996/captain/myimage) | +--------------------+ |
|
||||
|| || | | |
|
||||
|------------------| | as new ver's image +-----------+ +
|
||||
+------------------+ | | | CREATE
|
||||
+---------------------------------+ | img-captain--appname:5
|
||||
| | |
|
||||
| Set the Deployed Version | +-------------+ ^
|
||||
+---------------------------------+ | |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-----------------------+ | |
|
||||
| | | |
|
||||
| Docker ImageName +-------+ |
|
||||
| | |
|
||||
+-----------------------+ |
|
||||
+-------------------+ | | |
|
||||
| | | captain-definition +-------------+ |
|
||||
| | | content | | |
|
||||
| ServiceManager +----> CreateNewVersion +----> +-----------------------+ | |
|
||||
| | | | ^ |
|
||||
| | | Uploaded Tar +-----------------> ImageMaker. +
|
||||
+-------------------+ | | ^ createImage(appName,Ver,Data)
|
||||
+-----------------------+ |
|
||||
| | |
|
||||
| GIT Repo +-------------+
|
||||
| |
|
||||
+-----------------------+
|
||||
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const fs = require("fs-extra");
|
||||
const tar = require("tar");
|
||||
const path = require("path");
|
||||
const TemplateHelper = require("./TemplateHelper");
|
||||
const GitHelper = require("../utils/GitHelper");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const BuildLog = require("./BuildLog");
|
||||
const RAW_SOURCE_DIRECTORY = 'source_files';
|
||||
const TAR_FILE_NAME_READY_FOR_DOCKER = 'image.tar';
|
||||
const CAPTAIN_DEFINITION_FILE = 'captain-definition';
|
||||
const DOCKER_FILE = 'Dockerfile';
|
||||
class ImageMaker {
|
||||
constructor(dockerRegistryHelper, dockerApi, datastore, buildLogs, activeBuilds) {
|
||||
this.dockerRegistryHelper = dockerRegistryHelper;
|
||||
this.dockerApi = dockerApi;
|
||||
this.datastore = datastore;
|
||||
this.buildLogs = buildLogs;
|
||||
this.activeBuilds = activeBuilds;
|
||||
//
|
||||
}
|
||||
getDirectoryForRawSource(appName, version) {
|
||||
return (CaptainConstants.captainRawSourceDirectoryBase +
|
||||
'/' +
|
||||
appName +
|
||||
'/' +
|
||||
version);
|
||||
}
|
||||
/**
|
||||
* Creates image if necessary, or just simply passes the image name
|
||||
*/
|
||||
ensureImage(imageSource, appName, appVersion) {
|
||||
const self = this;
|
||||
this.activeBuilds[appName] = true;
|
||||
this.buildLogs[appName] =
|
||||
this.buildLogs[appName] ||
|
||||
new BuildLog(CaptainConstants.configs.buildLogSize);
|
||||
this.buildLogs[appName].clear();
|
||||
this.buildLogs[appName].log('------------------------- ' + new Date());
|
||||
this.buildLogs[appName].log('Build started for ' + appName);
|
||||
let gitHash = '';
|
||||
const baseDir = self.getDirectoryForRawSource(appName, appVersion);
|
||||
const rawDir = baseDir + '/' + RAW_SOURCE_DIRECTORY;
|
||||
const tarFilePath = baseDir + '/' + TAR_FILE_NAME_READY_FOR_DOCKER;
|
||||
const baseImageNameWithoutVerAndReg = self.datastore.getBuiltImageNameBase(appName); // img-captain--myapp
|
||||
let fullImageName = ''; // repo.domain.com:998/username/reponame:8
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return self.ensureDirectoryWithCaptainDefinition(imageSource, rawDir);
|
||||
})
|
||||
.then(function (gitHashFromImageSource) {
|
||||
gitHash = gitHashFromImageSource;
|
||||
// some users convert the directory into TAR instead of converting the content into TAR.
|
||||
// we go one level deep and try to find the right directory.
|
||||
return self.correctDirectoryAndEnsureCaptainDefinition(rawDir);
|
||||
})
|
||||
.then(function (correctedDir) {
|
||||
return self
|
||||
.getCaptainDefinition(correctedDir)
|
||||
.then(function (captainDefinition) {
|
||||
if (captainDefinition.imageName) {
|
||||
self.buildLogs[appName].log(`An explicit image name was provided (${captainDefinition.imageName}). Therefore, no build process is needed.`);
|
||||
self.buildLogs[appName].log(`The app (${appName}) will be re-deployed with this image: ${captainDefinition.imageName}`);
|
||||
return captainDefinition.imageName + '';
|
||||
}
|
||||
return self.getBuildPushAndReturnImageName(captainDefinition, correctedDir, tarFilePath, baseImageNameWithoutVerAndReg, appName, appVersion);
|
||||
});
|
||||
})
|
||||
.then(function (ret) {
|
||||
fullImageName = ret;
|
||||
})
|
||||
.then(function () {
|
||||
return fs.remove(baseDir);
|
||||
})
|
||||
.then(function () {
|
||||
if (imageSource.uploadedTarPathSource) {
|
||||
return fs.remove(imageSource.uploadedTarPathSource.uploadedTarPath);
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
return fs
|
||||
.remove(baseDir)
|
||||
.then(function () {
|
||||
throw new Error('ensure catch');
|
||||
})
|
||||
.catch(function () {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (imageSource.uploadedTarPathSource) {
|
||||
return fs
|
||||
.remove(imageSource.uploadedTarPathSource.uploadedTarPath)
|
||||
.then(function () {
|
||||
throw new Error('ensure catch');
|
||||
})
|
||||
.catch(function () {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
return Promise.reject(err);
|
||||
})
|
||||
.then(function () {
|
||||
self.activeBuilds[appName] = false;
|
||||
return {
|
||||
imageName: fullImageName,
|
||||
gitHash: gitHash,
|
||||
};
|
||||
})
|
||||
.catch(function (error) {
|
||||
self.activeBuilds[appName] = false;
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
getBuildPushAndReturnImageName(captainDefinition, correctedDirProvided, tarFilePath, baseImageNameWithoutVersionAndReg, appName, appVersion) {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return self
|
||||
.convertCaptainDefinitionToDockerfile(captainDefinition, correctedDirProvided)
|
||||
.then(function () {
|
||||
return self.convertContentOfDirectoryIntoTar(correctedDirProvided, tarFilePath);
|
||||
})
|
||||
.then(function () {
|
||||
return self.dockerApi
|
||||
.buildImageFromDockerFile(baseImageNameWithoutVersionAndReg, appVersion, tarFilePath, self.buildLogs[appName])
|
||||
.catch(function (error) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.BUILD_ERROR, ('' + error).trim());
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return self.dockerRegistryHelper.retagAndPushIfDefaultPushExist(baseImageNameWithoutVersionAndReg, appVersion, self.buildLogs[appName]);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns a promise that resolve to path a directory where source files + captain definition
|
||||
*
|
||||
* @param source the image source
|
||||
* @param destDirectory the path to directory where we want to have all our contents
|
||||
*/
|
||||
ensureDirectoryWithCaptainDefinition(source, destDirectory) {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return fs.ensureDir(destDirectory);
|
||||
})
|
||||
.then(function () {
|
||||
// If uploadedTarPath then extract into a directory
|
||||
//
|
||||
// If Repo then download.
|
||||
//
|
||||
// If captainDefinitionContent then create a directory and output to a directory
|
||||
//
|
||||
// Else THROW ERROR
|
||||
const srcTar = source.uploadedTarPathSource;
|
||||
if (srcTar) {
|
||||
// extract file to to destDirectory
|
||||
return tar
|
||||
.extract({
|
||||
file: srcTar.uploadedTarPath,
|
||||
cwd: destDirectory,
|
||||
})
|
||||
.then(function () {
|
||||
return srcTar.gitHash;
|
||||
});
|
||||
}
|
||||
const srcRepo = source.repoInfoSource;
|
||||
if (srcRepo) {
|
||||
return GitHelper.clone(srcRepo.user, srcRepo.password, srcRepo.repo, srcRepo.branch, destDirectory).then(function () {
|
||||
return GitHelper.getLastHash(destDirectory);
|
||||
});
|
||||
}
|
||||
const captainDefinitionContentSource = source.captainDefinitionContentSource;
|
||||
if (captainDefinitionContentSource) {
|
||||
return fs
|
||||
.outputFile(destDirectory + '/' + CAPTAIN_DEFINITION_FILE, captainDefinitionContentSource.captainDefinitionContent)
|
||||
.then(function () {
|
||||
return captainDefinitionContentSource.gitHash;
|
||||
});
|
||||
}
|
||||
// we should never get here!
|
||||
throw new Error('Source is unknown!');
|
||||
});
|
||||
}
|
||||
getAllChildrenOfDirectory(directory) {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readdir(directory, function (err, files) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
getCaptainDefinition(directoryWithCaptainDefinition) {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return fs.readJson(directoryWithCaptainDefinition +
|
||||
'/' +
|
||||
CAPTAIN_DEFINITION_FILE);
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Captain Definition File is empty!');
|
||||
}
|
||||
if (!data.schemaVersion) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Captain Definition version is empty!');
|
||||
}
|
||||
if (data.schemaVersion !== 2) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Captain Definition version is not supported! Read migration guides to schemaVersion 2');
|
||||
}
|
||||
const hasDockerfileLines = data.dockerfileLines && data.dockerfileLines.length > 0;
|
||||
let numberOfProperties = (!!data.templateId ? 1 : 0) +
|
||||
(!!data.imageName ? 1 : 0) +
|
||||
(!!data.dockerfilePath ? 1 : 0) +
|
||||
(hasDockerfileLines ? 1 : 0);
|
||||
if (numberOfProperties !== 1) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'One, and only one, of these properties should be present in captain-definition: templateId, imageName, dockerfilePath, or, dockerfileLines');
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
convertCaptainDefinitionToDockerfile(captainDefinition, directoryWithCaptainDefinition) {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
let data = captainDefinition;
|
||||
if (data.templateId) {
|
||||
return TemplateHelper.get().getDockerfileContentFromTemplateTag(data.templateId);
|
||||
}
|
||||
else if (data.dockerfileLines) {
|
||||
return data.dockerfileLines.join('\n');
|
||||
}
|
||||
else if (data.dockerfilePath) {
|
||||
if (data.dockerfilePath.startsWith('..')) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'dockerfilePath should not refer to parent directory!');
|
||||
}
|
||||
return fs
|
||||
.readFileSync(path.join(directoryWithCaptainDefinition, data.dockerfilePath))
|
||||
.toString();
|
||||
}
|
||||
else if (data.imageName) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'ImageName cannot be rebuilt');
|
||||
}
|
||||
else {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'dockerfileLines, dockerFilePath, templateId or imageName must be present. Both should not be present at the same time');
|
||||
}
|
||||
})
|
||||
.then(function (dockerfileContent) {
|
||||
return fs.outputFile(directoryWithCaptainDefinition + '/' + DOCKER_FILE, dockerfileContent);
|
||||
});
|
||||
}
|
||||
correctDirectoryAndEnsureCaptainDefinition(originalDirectory) {
|
||||
const self = this;
|
||||
function isCaptainDefinitionInDir(dir) {
|
||||
const pathToCheck = path.join(dir, CAPTAIN_DEFINITION_FILE);
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return fs.pathExists(pathToCheck);
|
||||
})
|
||||
.then(function (exits) {
|
||||
return !!exits && fs.statSync(pathToCheck).isFile();
|
||||
});
|
||||
}
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
// make sure if you need to go to child directory
|
||||
return isCaptainDefinitionInDir(originalDirectory);
|
||||
})
|
||||
.then(function (exists) {
|
||||
if (exists)
|
||||
return originalDirectory;
|
||||
// check if there is only one child
|
||||
// check if it's a directory
|
||||
// check if captain definition exists in it
|
||||
// if so, return the child directory
|
||||
return self
|
||||
.getAllChildrenOfDirectory(originalDirectory)
|
||||
.then(function (files) {
|
||||
files = files || [];
|
||||
if (files.length === 1) {
|
||||
return isCaptainDefinitionInDir(path.join(originalDirectory, files[0])) //
|
||||
.then(function (existsInChild) {
|
||||
if (existsInChild)
|
||||
return path.join(originalDirectory, files[0]);
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Captain Definition file does not exist!');
|
||||
});
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Captain Definition file does not exist!');
|
||||
});
|
||||
});
|
||||
}
|
||||
convertContentOfDirectoryIntoTar(sourceDirectory, tarFilePath) {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return tar.c({
|
||||
file: tarFilePath,
|
||||
cwd: sourceDirectory,
|
||||
}, ['./']);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = ImageMaker;
|
||||
//# sourceMappingURL=ImageMaker.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,488 +0,0 @@
|
||||
"use strict";
|
||||
const Logger = require("../utils/Logger");
|
||||
const CaptainConstants = require("../utils/CaptainConstants");
|
||||
const CaptainManager = require("./system/CaptainManager");
|
||||
const DockerApi_1 = require("../docker/DockerApi");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const requireFromString = require("require-from-string");
|
||||
const BuildLog = require("./BuildLog");
|
||||
const DockerRegistryHelper = require("./DockerRegistryHelper");
|
||||
const ImageMaker_1 = require("./ImageMaker");
|
||||
class ServiceManager {
|
||||
constructor(dataStore, dockerApi, loadBalancerManager) {
|
||||
this.dataStore = dataStore;
|
||||
this.dockerApi = dockerApi;
|
||||
this.loadBalancerManager = loadBalancerManager;
|
||||
this.activeBuilds = {};
|
||||
this.buildLogs = {};
|
||||
this.isReady = true;
|
||||
this.dockerRegistryHelper = new DockerRegistryHelper(this.dataStore, this.dockerApi);
|
||||
this.imageMaker = new ImageMaker_1.default(this.dockerRegistryHelper, this.dockerApi, this.dataStore, this.buildLogs, this.activeBuilds);
|
||||
}
|
||||
getRegistryHelper() {
|
||||
return this.dockerRegistryHelper;
|
||||
}
|
||||
isInited() {
|
||||
return this.isReady;
|
||||
}
|
||||
deployNewVersion(appName, source) {
|
||||
const self = this;
|
||||
const dataStore = this.dataStore;
|
||||
let deployedVersion;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return dataStore.getAppsDataStore().createNewVersion(appName);
|
||||
})
|
||||
.then(function (appVersion) {
|
||||
deployedVersion = appVersion;
|
||||
return self.imageMaker.ensureImage(source, appName, appVersion);
|
||||
})
|
||||
.then(function (builtImage) {
|
||||
return dataStore
|
||||
.getAppsDataStore()
|
||||
.setDeployedVersionAndImage(appName, deployedVersion, builtImage);
|
||||
})
|
||||
.then(function () {
|
||||
return self.ensureServiceInitedAndUpdated(appName);
|
||||
})
|
||||
.catch(function (error) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
self.logBuildFailed(appName, error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
enableCustomDomainSsl(appName, customDomain) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
Logger.d('Verifying Captain owns domain: ' + customDomain);
|
||||
return CaptainManager.get().verifyCaptainOwnsDomainOrThrow(customDomain, undefined);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Enabling SSL for: ' + appName + ' on ' + customDomain);
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.verifyCustomDomainBelongsToApp(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return CaptainManager.get().requestCertificateForDomain(customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.enableCustomDomainSsl(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
addCustomDomain(appName, customDomain) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
const rootDomain = self.dataStore.getRootDomain();
|
||||
const dotRootDomain = '.' + rootDomain;
|
||||
if (!customDomain || !/^[a-z0-9\-\.]+$/.test(customDomain)) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'Domain name is not accepted. Please use alphanumerical domains such as myapp.google123.ca');
|
||||
}
|
||||
if (customDomain.length > 80) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'Domain name is not accepted. Please use alphanumerical domains less than 80 characters in length.');
|
||||
}
|
||||
if (customDomain.indexOf('..') >= 0) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'Domain name is not accepted. You cannot have two consecutive periods ".." inside a domain name. Please use alphanumerical domains such as myapp.google123.ca');
|
||||
}
|
||||
if (customDomain.indexOf(dotRootDomain) >= 0 &&
|
||||
customDomain.indexOf(dotRootDomain) +
|
||||
dotRootDomain.length ===
|
||||
customDomain.length) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_BAD_NAME, 'Domain name is not accepted. Custom domain cannot be subdomain of root domain.');
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return CaptainManager.get().verifyDomainResolvesToDefaultServerOnHost(customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Enabling custom domain for: ' + appName);
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.addCustomDomainForApp(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
removeCustomDomain(appName, customDomain) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
Logger.d('Removing custom domain for: ' + appName);
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.removeCustomDomainForApp(appName, customDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
enableSslForApp(appName) {
|
||||
const self = this;
|
||||
let rootDomain;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.verifyCaptainOwnsGenericSubDomain(appName);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Enabling SSL for: ' + appName);
|
||||
return self.dataStore.getRootDomain();
|
||||
})
|
||||
.then(function (val) {
|
||||
rootDomain = val;
|
||||
if (!rootDomain) {
|
||||
throw new Error('No rootDomain! Cannot verify domain');
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
// it will ensure that the app exists, otherwise it throws an exception
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.getAppDefinition(appName);
|
||||
})
|
||||
.then(function () {
|
||||
return appName + '.' + rootDomain;
|
||||
})
|
||||
.then(function (domainName) {
|
||||
return CaptainManager.get().requestCertificateForDomain(domainName);
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.enableSslForDefaultSubDomain(appName);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
verifyCaptainOwnsGenericSubDomain(appName) {
|
||||
const self = this;
|
||||
let rootDomain;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getRootDomain();
|
||||
})
|
||||
.then(function (val) {
|
||||
rootDomain = val;
|
||||
})
|
||||
.then(function () {
|
||||
// it will ensure that the app exists, otherwise it throws an exception
|
||||
return self.dataStore
|
||||
.getAppsDataStore()
|
||||
.getAppDefinition(appName);
|
||||
})
|
||||
.then(function () {
|
||||
return appName + '.' + rootDomain;
|
||||
})
|
||||
.then(function (domainName) {
|
||||
Logger.d('Verifying Captain owns domain: ' + domainName);
|
||||
return CaptainManager.get().verifyCaptainOwnsDomainOrThrow(domainName, undefined);
|
||||
});
|
||||
}
|
||||
removeApp(appName) {
|
||||
Logger.d('Removing service for: ' + appName);
|
||||
const self = this;
|
||||
const serviceName = this.dataStore
|
||||
.getAppsDataStore()
|
||||
.getServiceName(appName);
|
||||
const dockerApi = this.dockerApi;
|
||||
const dataStore = this.dataStore;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
Logger.d('Check if service is running: ' + serviceName);
|
||||
return dockerApi.isServiceRunningByName(serviceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
if (isRunning) {
|
||||
return dockerApi.removeServiceByName(serviceName);
|
||||
}
|
||||
else {
|
||||
Logger.w('Cannot delete service... It is not running: ' +
|
||||
serviceName);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore.getAppsDataStore().deleteAppDefinition(appName);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
getUnusedImages(mostRecentLimit) {
|
||||
Logger.d('Getting unused images, excluding most recent ones: ' +
|
||||
mostRecentLimit);
|
||||
const self = this;
|
||||
const dockerApi = this.dockerApi;
|
||||
const dataStore = this.dataStore;
|
||||
let allImages;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dockerApi.getImages();
|
||||
})
|
||||
.then(function (images) {
|
||||
allImages = images;
|
||||
return dataStore.getAppsDataStore().getAppDefinitions();
|
||||
})
|
||||
.then(function (apps) {
|
||||
const unusedImages = [];
|
||||
if (mostRecentLimit < 0) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'Most Recent Limit cannot be negative');
|
||||
}
|
||||
for (let i = 0; i < allImages.length; i++) {
|
||||
const img = allImages[i];
|
||||
let imageInUse = false;
|
||||
const repoTags = img.RepoTags || [];
|
||||
Object.keys(apps).forEach(function (key, index) {
|
||||
const app = apps[key];
|
||||
const appName = key;
|
||||
for (let k = 0; k < mostRecentLimit + 1; k++) {
|
||||
const versionToCheck = Number(app.deployedVersion) - k;
|
||||
if (versionToCheck < 0)
|
||||
continue;
|
||||
let deployedImage = '';
|
||||
app.versions.forEach(v => {
|
||||
if (v.version === versionToCheck) {
|
||||
deployedImage = v.deployedImageName || '';
|
||||
}
|
||||
});
|
||||
if (!deployedImage)
|
||||
continue;
|
||||
if (repoTags.indexOf(deployedImage) >= 0) {
|
||||
imageInUse = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!imageInUse) {
|
||||
unusedImages.push({
|
||||
id: img.Id,
|
||||
tags: repoTags,
|
||||
});
|
||||
}
|
||||
}
|
||||
return unusedImages;
|
||||
});
|
||||
}
|
||||
deleteImages(imageIds) {
|
||||
Logger.d('Deleting images...');
|
||||
const self = this;
|
||||
const dockerApi = this.dockerApi;
|
||||
return Promise.resolve().then(function () {
|
||||
return dockerApi.deleteImages(imageIds);
|
||||
});
|
||||
}
|
||||
createPreDeployFunctionIfExist(app) {
|
||||
let preDeployFunction = app.preDeployFunction;
|
||||
if (!preDeployFunction) {
|
||||
return undefined;
|
||||
}
|
||||
/*
|
||||
////////////////////////////////// Expected content of the file //////////////////////////
|
||||
|
||||
const uuid = require('uuid/v4');
|
||||
console.log('-------------------------------'+uuid());
|
||||
|
||||
preDeployFunction = function (captainAppObj, dockerUpdateObject) {
|
||||
return Promise.resolve()
|
||||
.then(function(){
|
||||
console.log(JSON.stringify(dockerUpdateObject));
|
||||
return dockerUpdateObject;
|
||||
});
|
||||
};
|
||||
*/
|
||||
preDeployFunction =
|
||||
preDeployFunction + '\n\n module.exports = preDeployFunction';
|
||||
return requireFromString(preDeployFunction);
|
||||
}
|
||||
updateAppDefinition(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, forceSsl, ports, repoInfo, customNginxConfig, preDeployFunction) {
|
||||
const self = this;
|
||||
const dataStore = this.dataStore;
|
||||
const dockerApi = this.dockerApi;
|
||||
let serviceName;
|
||||
const checkIfNodeIdExists = function (nodeIdToCheck) {
|
||||
return dockerApi.getNodesInfo().then(function (nodeInfo) {
|
||||
for (let i = 0; i < nodeInfo.length; i++) {
|
||||
if (nodeIdToCheck === nodeInfo[i].nodeId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Node ID you requested in not part of the swarm ' +
|
||||
nodeIdToCheck);
|
||||
});
|
||||
};
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dataStore.getAppsDataStore().getAppDefinition(appName);
|
||||
})
|
||||
.then(function (app) {
|
||||
serviceName = dataStore
|
||||
.getAppsDataStore()
|
||||
.getServiceName(appName);
|
||||
// After leaving this block, nodeId will be guaranteed to be NonNull
|
||||
if (app.hasPersistentData) {
|
||||
if (nodeId) {
|
||||
return checkIfNodeIdExists(nodeId);
|
||||
}
|
||||
else {
|
||||
if (app.nodeId) {
|
||||
nodeId = app.nodeId;
|
||||
}
|
||||
else {
|
||||
return dockerApi
|
||||
.isServiceRunningByName(serviceName)
|
||||
.then(function (isRunning) {
|
||||
if (!isRunning) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Cannot find the service. Try again in a minute...');
|
||||
}
|
||||
return dockerApi.getNodeIdByServiceName(serviceName, 0);
|
||||
})
|
||||
.then(function (nodeIdRunningService) {
|
||||
if (!nodeIdRunningService) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'No NodeId was found. Try again in a minute...');
|
||||
}
|
||||
nodeId = nodeIdRunningService;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (volumes && volumes.length) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'Cannot set volumes for a non-persistent container!');
|
||||
}
|
||||
if (nodeId) {
|
||||
return checkIfNodeIdExists(nodeId);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore
|
||||
.getAppsDataStore()
|
||||
.updateAppDefinitionInDb(appName, instanceCount, envVars, volumes, nodeId, notExposeAsWebApp, forceSsl, ports, repoInfo, CaptainManager.getAuthenticator(dataStore.getNameSpace()), customNginxConfig, preDeployFunction);
|
||||
})
|
||||
.then(function () {
|
||||
return self.ensureServiceInitedAndUpdated(appName);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
isAppBuilding(appName) {
|
||||
return !!this.activeBuilds[appName];
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @returns the active build that it finds
|
||||
*/
|
||||
isAnyBuildRunning() {
|
||||
const activeBuilds = this.activeBuilds;
|
||||
for (const appName in activeBuilds) {
|
||||
if (!!activeBuilds[appName]) {
|
||||
return appName;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
getBuildStatus(appName) {
|
||||
const self = this;
|
||||
this.buildLogs[appName] =
|
||||
this.buildLogs[appName] ||
|
||||
new BuildLog(CaptainConstants.configs.buildLogSize);
|
||||
return {
|
||||
isAppBuilding: self.isAppBuilding(appName),
|
||||
logs: self.buildLogs[appName].getLogs(),
|
||||
isBuildFailed: self.buildLogs[appName].isBuildFailed,
|
||||
};
|
||||
}
|
||||
logBuildFailed(appName, error) {
|
||||
error = (error || '') + '';
|
||||
this.buildLogs[appName] =
|
||||
this.buildLogs[appName] ||
|
||||
new BuildLog(CaptainConstants.configs.buildLogSize);
|
||||
this.buildLogs[appName].onBuildFailed(error);
|
||||
}
|
||||
ensureServiceInitedAndUpdated(appName) {
|
||||
Logger.d('Ensure service inited and Updated for: ' + appName);
|
||||
const self = this;
|
||||
const serviceName = this.dataStore
|
||||
.getAppsDataStore()
|
||||
.getServiceName(appName);
|
||||
let imageName;
|
||||
const dockerApi = this.dockerApi;
|
||||
const dataStore = this.dataStore;
|
||||
let app;
|
||||
let dockerAuthObject;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return dataStore.getAppsDataStore().getAppDefinition(appName);
|
||||
})
|
||||
.then(function (appFound) {
|
||||
app = appFound;
|
||||
Logger.d(`Check if service is running: ${serviceName}`);
|
||||
return dockerApi.isServiceRunningByName(serviceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
for (let i = 0; i < app.versions.length; i++) {
|
||||
const element = app.versions[i];
|
||||
if (element.version === app.deployedVersion) {
|
||||
imageName = element.deployedImageName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!imageName) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_PARAMETER, 'ImageName for deployed version is not available, this version was probably failed due to an unsuccessful build!');
|
||||
}
|
||||
if (isRunning) {
|
||||
Logger.d('Service is already running: ' + serviceName);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Logger.d(`Creating service ${serviceName} with default image, we will update image later`);
|
||||
// if we pass in networks here. Almost always it results in a delayed update which causes
|
||||
// update errors if they happen right away!
|
||||
return dockerApi.createServiceOnNodeId(CaptainConstants.appPlaceholderImageName, serviceName, undefined, undefined, undefined, undefined, undefined);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self.dockerRegistryHelper.getDockerAuthObjectForImageName(imageName);
|
||||
})
|
||||
.then(function (data) {
|
||||
dockerAuthObject = data;
|
||||
})
|
||||
.then(function () {
|
||||
return self.createPreDeployFunctionIfExist(app);
|
||||
})
|
||||
.then(function (preDeployFunction) {
|
||||
Logger.d(`Updating service ${serviceName} with image ${imageName}`);
|
||||
return dockerApi.updateService(serviceName, imageName, app.volumes, app.networks, app.envVars, undefined, dockerAuthObject, Number(app.instanceCount), app.nodeId, dataStore.getNameSpace(), app.ports, app, DockerApi_1.IDockerUpdateOrders.AUTO, preDeployFunction);
|
||||
})
|
||||
.then(function () {
|
||||
return new Promise(function (resolve) {
|
||||
// Waiting 2 extra seconds for docker DNS to pickup the service name
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer();
|
||||
});
|
||||
}
|
||||
reloadLoadBalancer() {
|
||||
Logger.d('Updating Load Balancer');
|
||||
const self = this;
|
||||
return self.loadBalancerManager
|
||||
.rePopulateNginxConfigFile(self.dataStore)
|
||||
.then(function () {
|
||||
Logger.d('sendReloadSignal...');
|
||||
return self.loadBalancerManager.sendReloadSignal();
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = ServiceManager;
|
||||
//# sourceMappingURL=ServiceManager.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,68 +0,0 @@
|
||||
"use strict";
|
||||
const fs = require("fs-extra");
|
||||
const ApiStatusCodes = require("../api/ApiStatusCodes");
|
||||
const TemplateHelperVersionPrinter = require("../utils/TemplateHelperVersionPrinter");
|
||||
class TemplateHelper {
|
||||
constructor() {
|
||||
const templates = [
|
||||
{
|
||||
templateName: 'node',
|
||||
dockerHubImageName: 'library/node',
|
||||
tagSuffix: '-alpine',
|
||||
},
|
||||
{
|
||||
templateName: 'php',
|
||||
dockerHubImageName: 'library/php',
|
||||
tagSuffix: '-apache',
|
||||
},
|
||||
{
|
||||
templateName: 'python-django',
|
||||
dockerHubImageName: 'library/python',
|
||||
tagSuffix: '-alpine3.6',
|
||||
},
|
||||
{
|
||||
templateName: 'ruby-rack',
|
||||
dockerHubImageName: 'library/ruby',
|
||||
tagSuffix: '-alpine3.7',
|
||||
},
|
||||
];
|
||||
const dockerfilesRoot = __dirname + '/../../dockerfiles/';
|
||||
for (let i = 0; i < templates.length; i++) {
|
||||
templates[i].postFromLines = fs.readFileSync(dockerfilesRoot + templates[i].templateName, 'utf8');
|
||||
}
|
||||
this.templates = templates;
|
||||
// Change to true if you want tags to be printed on screen upon start up (after 40 sec ish)
|
||||
if (false) {
|
||||
new TemplateHelperVersionPrinter().printAvailableImageTagsForReadme(this.templates);
|
||||
}
|
||||
}
|
||||
getTemplateFromTemplateName(templateName) {
|
||||
for (let i = 0; i < this.templates.length; i++) {
|
||||
if (this.templates[i].templateName === templateName) {
|
||||
return this.templates[i];
|
||||
}
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'TEMPLATE NAME NOT FOUND: ' + templateName);
|
||||
}
|
||||
getDockerfileContentFromTemplateTag(templateAndVersion) {
|
||||
const self = this;
|
||||
const templateName = templateAndVersion.split('/')[0];
|
||||
const templateVersion = templateAndVersion.split('/')[1];
|
||||
if (!templateVersion) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Template version field is empty!');
|
||||
}
|
||||
const templateObj = self.getTemplateFromTemplateName(templateName);
|
||||
const fromLine = templateObj.dockerHubImageName +
|
||||
':' +
|
||||
templateVersion +
|
||||
templateObj.tagSuffix;
|
||||
return 'FROM ' + fromLine + '\n' + templateObj.postFromLines;
|
||||
}
|
||||
}
|
||||
const templateHelperInstance = new TemplateHelper();
|
||||
module.exports = {
|
||||
get: function () {
|
||||
return templateHelperInstance;
|
||||
},
|
||||
};
|
||||
//# sourceMappingURL=TemplateHelper.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"TemplateHelper.js","sourceRoot":"","sources":["../../src/user/TemplateHelper.ts"],"names":[],"mappings":";AACA,+BAA+B;AAC/B,wDAAwD;AAIxD,sFAAsF;AAEtF,MAAM,cAAc;IAGhB;QACI,MAAM,SAAS,GAAgB;YAC3B;gBACI,YAAY,EAAE,MAAM;gBACpB,kBAAkB,EAAE,cAAc;gBAClC,SAAS,EAAE,SAAS;aACvB;YACD;gBACI,YAAY,EAAE,KAAK;gBACnB,kBAAkB,EAAE,aAAa;gBACjC,SAAS,EAAE,SAAS;aACvB;YACD;gBACI,YAAY,EAAE,eAAe;gBAC7B,kBAAkB,EAAE,gBAAgB;gBACpC,SAAS,EAAE,YAAY;aAC1B;YACD;gBACI,YAAY,EAAE,WAAW;gBACzB,kBAAkB,EAAE,cAAc;gBAClC,SAAS,EAAE,YAAY;aAC1B;SACJ,CAAA;QAED,MAAM,eAAe,GAAG,SAAS,GAAG,qBAAqB,CAAA;QAEzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACvC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,EAAE,CAAC,YAAY,CACxC,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,EAC3C,MAAM,CACT,CAAA;SACJ;QAED,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAE1B,2FAA2F;QAC3F,IAAI,KAAK,EAAE;YACP,IAAI,4BAA4B,EAAE,CAAC,gCAAgC,CAC/D,IAAI,CAAC,SAAS,CACjB,CAAA;SACJ;IACL,CAAC;IAED,2BAA2B,CAAC,YAAoB;QAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC5C,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,EAAE;gBACjD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;aAC3B;SACJ;QACD,MAAM,cAAc,CAAC,WAAW,CAC5B,cAAc,CAAC,oBAAoB,EACnC,2BAA2B,GAAG,YAAY,CAC7C,CAAA;IACL,CAAC;IAED,mCAAmC,CAAC,kBAA0B;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAA;QACjB,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACrD,MAAM,eAAe,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACxD,IAAI,CAAC,eAAe,EAAE;YAClB,MAAM,cAAc,CAAC,WAAW,CAC5B,cAAc,CAAC,oBAAoB,EACnC,kCAAkC,CACrC,CAAA;SACJ;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,2BAA2B,CAAC,YAAY,CAAC,CAAA;QAElE,MAAM,QAAQ,GACV,WAAW,CAAC,kBAAkB;YAC9B,GAAG;YACH,eAAe;YACf,WAAW,CAAC,SAAS,CAAA;QAEzB,OAAO,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,WAAW,CAAC,aAAa,CAAA;IAChE,CAAC;CACJ;AAED,MAAM,sBAAsB,GAAG,IAAI,cAAc,EAAE,CAAA;AAEnD,iBAAS;IACL,GAAG,EAAE;QACD,OAAO,sBAAsB,CAAA;IACjC,CAAC;CACJ,CAAA"}
|
||||
@@ -1,723 +0,0 @@
|
||||
"use strict";
|
||||
const uuid = require("uuid/v4");
|
||||
const SshClientImport = require("ssh2");
|
||||
const request = require("request");
|
||||
const fs = require("fs-extra");
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const LoadBalancerManager = require("./LoadBalancerManager");
|
||||
const CertbotManager = require("./CertbotManager");
|
||||
const SelfHostedDockerRegistry = require("./SelfHostedDockerRegistry");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const DataStoreProvider = require("../../datastore/DataStoreProvider");
|
||||
const DockerApi_1 = require("../../docker/DockerApi");
|
||||
const IRegistryInfo_1 = require("../../models/IRegistryInfo");
|
||||
const MigrateCaptainDuckDuck_1 = require("../../utils/MigrateCaptainDuckDuck");
|
||||
const Authenticator = require("../Authenticator");
|
||||
const DEBUG_SALT = 'THIS IS NOT A REAL CERTIFICATE';
|
||||
const SshClient = SshClientImport.Client;
|
||||
const MAX_FAIL_ALLOWED = 4;
|
||||
const HEALTH_CHECK_INTERVAL = 20000; // ms
|
||||
const TIMEOUT_HEALTH_CHECK = 15000; // ms
|
||||
class CaptainManager {
|
||||
constructor() {
|
||||
const dockerApi = DockerApi_1.default.get();
|
||||
this.hasForceSsl = false;
|
||||
this.dataStore = DataStoreProvider.getDataStore(CaptainConstants.rootNameSpace);
|
||||
this.dockerApi = dockerApi;
|
||||
this.certbotManager = new CertbotManager(dockerApi);
|
||||
this.loadBalancerManager = new LoadBalancerManager(dockerApi, this.certbotManager, this.dataStore);
|
||||
this.myNodeId = undefined;
|
||||
this.inited = false;
|
||||
this.waitUntilRestarted = false;
|
||||
this.captainSalt = '';
|
||||
this.consecutiveHealthCheckFailCount = 0;
|
||||
this.healthCheckUuid = uuid();
|
||||
}
|
||||
initialize() {
|
||||
// If a linked file / directory is deleted on the host, it loses the connection to
|
||||
// the container and needs an update to be picked up again.
|
||||
const self = this;
|
||||
const dataStore = this.dataStore;
|
||||
const dockerApi = this.dockerApi;
|
||||
const loadBalancerManager = this.loadBalancerManager;
|
||||
const certbotManager = this.certbotManager;
|
||||
let myNodeId;
|
||||
self.refreshForceSslState()
|
||||
.then(function () {
|
||||
return dockerApi.getNodeIdByServiceName(CaptainConstants.captainServiceName, 0);
|
||||
})
|
||||
.then(function (nodeId) {
|
||||
myNodeId = nodeId;
|
||||
self.myNodeId = myNodeId;
|
||||
self.dockerRegistry = new SelfHostedDockerRegistry(self.dockerApi, self.dataStore, self.certbotManager, self.loadBalancerManager, self.myNodeId);
|
||||
return dockerApi.isNodeManager(myNodeId);
|
||||
})
|
||||
.then(function (isManager) {
|
||||
if (!isManager) {
|
||||
throw new Error('Captain should only run on a manager node');
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Emptying generated and temp folders.');
|
||||
return fs.emptyDir(CaptainConstants.captainRootDirectoryTemp);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.emptyDir(CaptainConstants.captainRootDirectoryGenerated);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Ensuring directories are available on host. Started.');
|
||||
return fs.ensureDir(CaptainConstants.letsEncryptEtcPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.letsEncryptLibPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.captainStaticFilesDir);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.perAppNginxConfigPathBase);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureFile(CaptainConstants.baseNginxConfigPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.registryPathOnHost);
|
||||
})
|
||||
.then(function () {
|
||||
return dockerApi.ensureOverlayNetwork(CaptainConstants.captainNetworkName);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Ensuring directories are available on host. Finished.');
|
||||
return dockerApi.ensureServiceConnectedToNetwork(CaptainConstants.captainServiceName, CaptainConstants.captainNetworkName);
|
||||
})
|
||||
.then(function () {
|
||||
return loadBalancerManager.init(myNodeId, dataStore);
|
||||
})
|
||||
.then(function () {
|
||||
const valueIfNotExist = CaptainConstants.isDebug
|
||||
? DEBUG_SALT
|
||||
: uuid();
|
||||
return dockerApi.ensureSecret(CaptainConstants.captainSaltSecretKey, valueIfNotExist);
|
||||
})
|
||||
.then(function () {
|
||||
return dockerApi.ensureSecretOnService(CaptainConstants.captainServiceName, CaptainConstants.captainSaltSecretKey);
|
||||
})
|
||||
.then(function (secretHadExistedBefore) {
|
||||
if (!secretHadExistedBefore) {
|
||||
return new Promise(function () {
|
||||
Logger.d('I am halting here. I expect to get restarted in a few seconds due to a secret (captain salt) being updated.');
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
const secretFileName = '/run/secrets/' + CaptainConstants.captainSaltSecretKey;
|
||||
if (!fs.pathExistsSync(secretFileName)) {
|
||||
throw new Error('Secret is attached according to Docker. But file cannot be found. ' +
|
||||
secretFileName);
|
||||
}
|
||||
const secretContent = fs.readFileSync(secretFileName).toString();
|
||||
if (!secretContent) {
|
||||
throw new Error('Salt secret content is empty!');
|
||||
}
|
||||
self.captainSalt = secretContent;
|
||||
return true;
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore.setEncryptionSalt(self.getCaptainSalt());
|
||||
})
|
||||
.then(function () {
|
||||
return new MigrateCaptainDuckDuck_1.default(dataStore, CaptainManager.getAuthenticator(dataStore.getNameSpace()))
|
||||
.migrateIfNeeded()
|
||||
.then(function (migrationPerformed) {
|
||||
if (!!migrationPerformed) {
|
||||
return self.resetSelf();
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return certbotManager.init(myNodeId);
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore.getRegistriesDataStore().getAllRegistries();
|
||||
})
|
||||
.then(function (registries) {
|
||||
let localRegistry = undefined;
|
||||
for (let idx = 0; idx < registries.length; idx++) {
|
||||
const element = registries[idx];
|
||||
if (element.registryType === IRegistryInfo_1.IRegistryTypes.LOCAL_REG) {
|
||||
localRegistry = element;
|
||||
}
|
||||
}
|
||||
if (!!localRegistry) {
|
||||
Logger.d('Ensuring Docker Registry is running...');
|
||||
return self.dockerRegistry.ensureDockerRegistryRunningOnThisNode(localRegistry.registryPassword);
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.then(function () {
|
||||
self.inited = true;
|
||||
self.performHealthCheck();
|
||||
Logger.d('**** Captain is initialized and ready to serve you! ****');
|
||||
})
|
||||
.catch(function (error) {
|
||||
Logger.e(error);
|
||||
setTimeout(function () {
|
||||
process.exit(0);
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
performHealthCheck() {
|
||||
const self = this;
|
||||
const captainPublicDomain = CaptainConstants.captainSubDomain +
|
||||
'.' +
|
||||
self.dataStore.getRootDomain();
|
||||
function scheduleNextHealthCheck() {
|
||||
self.healthCheckUuid = uuid();
|
||||
setTimeout(function () {
|
||||
self.performHealthCheck();
|
||||
}, HEALTH_CHECK_INTERVAL);
|
||||
}
|
||||
// For debug build, we'll turn off health check
|
||||
if (CaptainConstants.isDebug || !self.dataStore.hasCustomDomain()) {
|
||||
scheduleNextHealthCheck();
|
||||
return;
|
||||
}
|
||||
function checkCaptainHealth(callback) {
|
||||
let callbackCalled = false;
|
||||
setTimeout(function () {
|
||||
if (callbackCalled) {
|
||||
return;
|
||||
}
|
||||
callbackCalled = true;
|
||||
callback(false);
|
||||
}, TIMEOUT_HEALTH_CHECK);
|
||||
const url = 'http://' +
|
||||
captainPublicDomain +
|
||||
CaptainConstants.healthCheckEndPoint;
|
||||
request(url, function (error, response, body) {
|
||||
if (callbackCalled) {
|
||||
return;
|
||||
}
|
||||
callbackCalled = true;
|
||||
if (error || !body || body !== self.getHealthCheckUuid()) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkNginxHealth(callback) {
|
||||
let callbackCalled = false;
|
||||
setTimeout(function () {
|
||||
if (callbackCalled) {
|
||||
return;
|
||||
}
|
||||
callbackCalled = true;
|
||||
callback(false);
|
||||
}, TIMEOUT_HEALTH_CHECK);
|
||||
self.verifyCaptainOwnsDomainOrThrow(captainPublicDomain, '-healthcheck')
|
||||
.then(function () {
|
||||
if (callbackCalled) {
|
||||
return;
|
||||
}
|
||||
callbackCalled = true;
|
||||
callback(true);
|
||||
})
|
||||
.catch(function () {
|
||||
if (callbackCalled) {
|
||||
return;
|
||||
}
|
||||
callbackCalled = true;
|
||||
callback(false);
|
||||
});
|
||||
}
|
||||
const checksPerformed = {};
|
||||
function scheduleIfNecessary() {
|
||||
if (!checksPerformed.captainHealth ||
|
||||
!checksPerformed.nginxHealth) {
|
||||
return;
|
||||
}
|
||||
let hasFailedCheck = false;
|
||||
if (!checksPerformed.captainHealth.value) {
|
||||
Logger.w('Captain health check failed: #' +
|
||||
self.consecutiveHealthCheckFailCount +
|
||||
' at ' +
|
||||
captainPublicDomain);
|
||||
hasFailedCheck = true;
|
||||
}
|
||||
if (!checksPerformed.nginxHealth.value) {
|
||||
Logger.w('NGINX health check failed: #' +
|
||||
self.consecutiveHealthCheckFailCount);
|
||||
hasFailedCheck = true;
|
||||
}
|
||||
if (hasFailedCheck) {
|
||||
self.consecutiveHealthCheckFailCount =
|
||||
self.consecutiveHealthCheckFailCount + 1;
|
||||
}
|
||||
else {
|
||||
self.consecutiveHealthCheckFailCount = 0;
|
||||
}
|
||||
scheduleNextHealthCheck();
|
||||
if (self.consecutiveHealthCheckFailCount > MAX_FAIL_ALLOWED) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
checkCaptainHealth(function (success) {
|
||||
checksPerformed.captainHealth = {
|
||||
value: success,
|
||||
};
|
||||
scheduleIfNecessary();
|
||||
});
|
||||
checkNginxHealth(function (success) {
|
||||
checksPerformed.nginxHealth = {
|
||||
value: success,
|
||||
};
|
||||
scheduleIfNecessary();
|
||||
});
|
||||
}
|
||||
getHealthCheckUuid() {
|
||||
return this.healthCheckUuid;
|
||||
}
|
||||
isInitialized() {
|
||||
return this.inited && !this.waitUntilRestarted;
|
||||
}
|
||||
getCaptainImageTags() {
|
||||
const url = 'https://hub.docker.com/v2/repositories/' +
|
||||
CaptainConstants.configs.publishedNameOnDockerHub +
|
||||
'/tags';
|
||||
return new Promise(function (resolve, reject) {
|
||||
request(url, function (error, response, body) {
|
||||
if (CaptainConstants.isDebug) {
|
||||
resolve(['v0.0.1']);
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
else if (!body || !JSON.parse(body).results) {
|
||||
reject(new Error('Received empty body or no result for version list on docker hub.'));
|
||||
}
|
||||
else {
|
||||
const results = JSON.parse(body).results;
|
||||
const tags = [];
|
||||
for (let idx = 0; idx < results.length; idx++) {
|
||||
tags.push(results[idx].name);
|
||||
}
|
||||
resolve(tags);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
updateCaptain(versionTag) {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.dockerApi.updateService(CaptainConstants.captainServiceName, CaptainConstants.configs.publishedNameOnDockerHub +
|
||||
':' +
|
||||
versionTag, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
});
|
||||
}
|
||||
getMyNodeId() {
|
||||
if (!this.myNodeId) {
|
||||
const msg = 'myNodeId is not set yet!!';
|
||||
Logger.e(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
return this.myNodeId;
|
||||
}
|
||||
getCaptainSalt() {
|
||||
if (!this.captainSalt) {
|
||||
const msg = 'Captain Salt is not set yet!!';
|
||||
Logger.e(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
return this.captainSalt;
|
||||
}
|
||||
updateNetDataInfo(netDataInfo) {
|
||||
const self = this;
|
||||
const dockerApi = this.dockerApi;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dockerApi.ensureContainerStoppedAndRemoved(CaptainConstants.netDataContainerName, CaptainConstants.captainNetworkName);
|
||||
})
|
||||
.then(function () {
|
||||
if (netDataInfo.isEnabled) {
|
||||
const vols = [
|
||||
{
|
||||
hostPath: '/proc',
|
||||
containerPath: '/host/proc',
|
||||
mode: 'ro',
|
||||
},
|
||||
{
|
||||
hostPath: '/sys',
|
||||
containerPath: '/host/sys',
|
||||
mode: 'ro',
|
||||
},
|
||||
{
|
||||
hostPath: '/var/run/docker.sock',
|
||||
containerPath: '/var/run/docker.sock',
|
||||
},
|
||||
];
|
||||
const envVars = [];
|
||||
if (netDataInfo.data.smtp) {
|
||||
envVars.push({
|
||||
key: 'SSMTP_TO',
|
||||
value: netDataInfo.data.smtp.to,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_HOSTNAME',
|
||||
value: netDataInfo.data.smtp.hostname,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_SERVER',
|
||||
value: netDataInfo.data.smtp.server,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_PORT',
|
||||
value: netDataInfo.data.smtp.port,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_TLS',
|
||||
value: netDataInfo.data.smtp.allowNonTls
|
||||
? 'NO'
|
||||
: 'YES',
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_USER',
|
||||
value: netDataInfo.data.smtp.username,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SSMTP_PASS',
|
||||
value: netDataInfo.data.smtp.password,
|
||||
});
|
||||
}
|
||||
if (netDataInfo.data.slack) {
|
||||
envVars.push({
|
||||
key: 'SLACK_WEBHOOK_URL',
|
||||
value: netDataInfo.data.slack.hook,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'SLACK_CHANNEL',
|
||||
value: netDataInfo.data.slack.channel,
|
||||
});
|
||||
}
|
||||
if (netDataInfo.data.telegram) {
|
||||
envVars.push({
|
||||
key: 'TELEGRAM_BOT_TOKEN',
|
||||
value: netDataInfo.data.telegram.botToken,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'TELEGRAM_CHAT_ID',
|
||||
value: netDataInfo.data.telegram.chatId,
|
||||
});
|
||||
}
|
||||
if (netDataInfo.data.pushBullet) {
|
||||
envVars.push({
|
||||
key: 'PUSHBULLET_ACCESS_TOKEN',
|
||||
value: netDataInfo.data.pushBullet.apiToken,
|
||||
});
|
||||
envVars.push({
|
||||
key: 'PUSHBULLET_DEFAULT_EMAIL',
|
||||
value: netDataInfo.data.pushBullet.fallbackEmail,
|
||||
});
|
||||
}
|
||||
return dockerApi.createStickyContainer(CaptainConstants.netDataContainerName, CaptainConstants.netDataImageName, vols, CaptainConstants.captainNetworkName, envVars, ['SYS_PTRACE']);
|
||||
}
|
||||
// Just removing the old container. No need to create a new one.
|
||||
return true;
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore.setNetDataInfo(netDataInfo);
|
||||
});
|
||||
}
|
||||
getNodesInfo() {
|
||||
const dockerApi = this.dockerApi;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dockerApi.getNodesInfo();
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data || !data.length) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'No cluster node was found!');
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
joinDockerNode(captainIpAddress, isManager, remoteNodeIpAddress, privateKey) {
|
||||
const remoteUserName = 'root'; // Docker requires root access. It has to be root.
|
||||
const dockerApi = this.dockerApi;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dockerApi.getJoinToken(isManager);
|
||||
})
|
||||
.then(function (token) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const conn = new SshClient();
|
||||
conn.on('error', function (err) {
|
||||
Logger.e(err);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'SSH Connection error!!'));
|
||||
})
|
||||
.on('ready', function () {
|
||||
Logger.d('SSH Client :: ready');
|
||||
conn.exec(dockerApi.createJoinCommand(captainIpAddress, token), function (err, stream) {
|
||||
if (err) {
|
||||
Logger.e(err);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'SSH Running command failed!!'));
|
||||
return;
|
||||
}
|
||||
let hasExisted = false;
|
||||
stream
|
||||
.on('close', function (code, signal) {
|
||||
Logger.d('Stream :: close :: code: ' +
|
||||
code +
|
||||
', signal: ' +
|
||||
signal);
|
||||
conn.end();
|
||||
if (hasExisted) {
|
||||
return;
|
||||
}
|
||||
hasExisted = true;
|
||||
resolve();
|
||||
})
|
||||
.on('data', function (data) {
|
||||
Logger.d('STDOUT: ' + data);
|
||||
})
|
||||
.stderr.on('data', function (data) {
|
||||
Logger.e('STDERR: ' + data);
|
||||
if (hasExisted) {
|
||||
return;
|
||||
}
|
||||
hasExisted = true;
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Error during setup: ' +
|
||||
data));
|
||||
});
|
||||
});
|
||||
})
|
||||
.connect({
|
||||
host: remoteNodeIpAddress,
|
||||
port: 22,
|
||||
username: remoteUserName,
|
||||
privateKey: privateKey,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
getLoadBalanceManager() {
|
||||
return this.loadBalancerManager;
|
||||
}
|
||||
reloadLoadBalancer(datastore) {
|
||||
const self = this;
|
||||
return self.loadBalancerManager
|
||||
.rePopulateNginxConfigFile(datastore)
|
||||
.then(function () {
|
||||
Logger.d('sendReloadSignal...');
|
||||
return self.loadBalancerManager.sendReloadSignal();
|
||||
});
|
||||
}
|
||||
getDockerRegistry() {
|
||||
return this.dockerRegistry;
|
||||
}
|
||||
enableSsl(emailAddress) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.certbotManager.ensureRegistered(emailAddress);
|
||||
})
|
||||
.then(function () {
|
||||
return self.certbotManager.enableSsl(CaptainConstants.captainSubDomain +
|
||||
'.' +
|
||||
self.dataStore.getRootDomain());
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore.setUserEmailAddress(emailAddress);
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore.setHasRootSsl(true);
|
||||
})
|
||||
.then(function () {
|
||||
return self.loadBalancerManager.rePopulateNginxConfigFile(self.dataStore);
|
||||
})
|
||||
.then(function () {
|
||||
return self.loadBalancerManager.sendReloadSignal();
|
||||
});
|
||||
}
|
||||
forceSsl(isEnabled) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getHasRootSsl();
|
||||
})
|
||||
.then(function (hasRootSsl) {
|
||||
if (!hasRootSsl) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'You first need to enable SSL on the root domain before forcing it.');
|
||||
}
|
||||
return self.dataStore.setForceSsl(isEnabled);
|
||||
})
|
||||
.then(function () {
|
||||
return self.refreshForceSslState();
|
||||
});
|
||||
}
|
||||
refreshForceSslState() {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getForceSsl();
|
||||
})
|
||||
.then(function (hasForceSsl) {
|
||||
self.hasForceSsl = hasForceSsl;
|
||||
});
|
||||
}
|
||||
getForceSslValue() {
|
||||
return !!this.hasForceSsl;
|
||||
}
|
||||
/**
|
||||
* Returns a promise successfully if verification is succeeded. If it fails, it throws an exception.
|
||||
*
|
||||
* @param domainName the domain to verify, app.mycaptainroot.com or www.myawesomeapp.com
|
||||
* @param identifierSuffix an optional suffix to be added to the identifier file name to avoid name conflict
|
||||
*
|
||||
* @returns {Promise.<boolean>}
|
||||
*/
|
||||
verifyCaptainOwnsDomainOrThrow(domainName, identifierSuffix) {
|
||||
const self = this;
|
||||
const randomUuid = uuid();
|
||||
const captainConfirmationPath = CaptainConstants.captainConfirmationPath +
|
||||
(identifierSuffix ? identifierSuffix : '');
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.certbotManager.domainValidOrThrow(domainName);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.outputFile(CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDomainSpecificHtmlDir +
|
||||
'/' +
|
||||
domainName +
|
||||
captainConfirmationPath, randomUuid);
|
||||
})
|
||||
.then(function () {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const url = 'http://' +
|
||||
domainName +
|
||||
':' +
|
||||
CaptainConstants.nginxPortNumber +
|
||||
captainConfirmationPath;
|
||||
request(url, function (error, response, body) {
|
||||
if (error || !body || body !== randomUuid) {
|
||||
Logger.e('Verification Failed for ' + domainName);
|
||||
Logger.e('Error ' + error);
|
||||
Logger.e('body ' + body);
|
||||
Logger.e('randomUuid ' + randomUuid);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.VERIFICATION_FAILED, 'Verification Failed.'));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
getNginxConfig() {
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
return self.dataStore.getNginxConfig();
|
||||
});
|
||||
}
|
||||
setNginxConfig(baseConfig, captainConfig) {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.setNginxConfig(baseConfig, captainConfig);
|
||||
})
|
||||
.then(function () {
|
||||
self.resetSelf();
|
||||
});
|
||||
}
|
||||
requestCertificateForDomain(domainName) {
|
||||
return this.certbotManager.enableSsl(domainName);
|
||||
}
|
||||
verifyDomainResolvesToDefaultServerOnHost(domainName) {
|
||||
const self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
const url = 'http://' +
|
||||
domainName +
|
||||
CaptainConstants.captainConfirmationPath;
|
||||
Logger.d('Sending request to ' + url);
|
||||
request(url, function (error, response, body) {
|
||||
if (error ||
|
||||
!body ||
|
||||
body !==
|
||||
self.loadBalancerManager.getCaptainPublicRandomKey()) {
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.VERIFICATION_FAILED, 'Verification Failed.'));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
changeCaptainRootDomain(requestedCustomDomain) {
|
||||
const self = this;
|
||||
// Some DNS servers do not allow wild cards. Therefore this line may fail.
|
||||
// We still allow users to specify the domains in their DNS settings individually
|
||||
// SubDomains that need to be added are "captain." "registry." "app-name."
|
||||
const url = (CaptainConstants.configs.preCheckForWildCard
|
||||
? uuid()
|
||||
: CaptainConstants.captainSubDomain) +
|
||||
'.' +
|
||||
requestedCustomDomain +
|
||||
':' +
|
||||
CaptainConstants.nginxPortNumber;
|
||||
return self
|
||||
.verifyDomainResolvesToDefaultServerOnHost(url)
|
||||
.then(function () {
|
||||
return self.dataStore.getHasRootSsl();
|
||||
})
|
||||
.then(function (hasRootSsl) {
|
||||
if (hasRootSsl &&
|
||||
self.dataStore.getRootDomain() !== requestedCustomDomain) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'SSL is enabled for root. Too late to change your mind!');
|
||||
}
|
||||
return self.dataStore.setCustomDomain(requestedCustomDomain);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reloadLoadBalancer(self.dataStore);
|
||||
});
|
||||
}
|
||||
resetSelf() {
|
||||
const self = this;
|
||||
Logger.d('Captain is resetting itself!');
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
const promiseToIgnore = self.dockerApi.updateService(CaptainConstants.captainServiceName, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
static getAuthenticator(namespace) {
|
||||
const authenticatorCache = CaptainManager.authenticatorCache;
|
||||
if (!namespace) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_NOT_AUTHORIZED, 'Empty namespace');
|
||||
}
|
||||
if (!authenticatorCache[namespace]) {
|
||||
const captainSalt = CaptainManager.get().getCaptainSalt();
|
||||
if (captainSalt) {
|
||||
authenticatorCache[namespace] = new Authenticator(captainSalt, namespace);
|
||||
}
|
||||
}
|
||||
return authenticatorCache[namespace];
|
||||
}
|
||||
static get() {
|
||||
if (!CaptainManager.captainManagerInstance) {
|
||||
CaptainManager.captainManagerInstance = new CaptainManager();
|
||||
}
|
||||
return CaptainManager.captainManagerInstance;
|
||||
}
|
||||
}
|
||||
CaptainManager.authenticatorCache = {};
|
||||
module.exports = CaptainManager;
|
||||
//# sourceMappingURL=CaptainManager.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,295 +0,0 @@
|
||||
"use strict";
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const fs = require("fs-extra");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const WEBROOT_PATH_IN_CERTBOT = '/captain-webroot';
|
||||
const WEBROOT_PATH_IN_CAPTAIN = CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDomainSpecificHtmlDir;
|
||||
const shouldUseStaging = false; // CaptainConstants.isDebug;
|
||||
class CertbotManager {
|
||||
constructor(dockerApi) {
|
||||
this.dockerApi = dockerApi;
|
||||
this.dockerApi = dockerApi;
|
||||
}
|
||||
domainValidOrThrow(domainName) {
|
||||
if (!domainName) {
|
||||
throw new Error('Domain Name is empty');
|
||||
}
|
||||
const RegExpression = /^[a-z0-9\.\-]*$/;
|
||||
if (!RegExpression.test(domainName)) {
|
||||
throw new Error('Bad Domain Name!');
|
||||
}
|
||||
}
|
||||
getCertRelativePathForDomain(domainName) {
|
||||
const self = this;
|
||||
self.domainValidOrThrow(domainName);
|
||||
return '/live/' + domainName + '/fullchain.pem';
|
||||
}
|
||||
getKeyRelativePathForDomain(domainName) {
|
||||
const self = this;
|
||||
self.domainValidOrThrow(domainName);
|
||||
return '/live/' + domainName + '/privkey.pem';
|
||||
}
|
||||
enableSsl(domainName) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const self = this;
|
||||
Logger.d('Enabling SSL for ' + domainName);
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
self.domainValidOrThrow(domainName);
|
||||
return self.ensureDomainHasDirectory(domainName);
|
||||
})
|
||||
.then(function () {
|
||||
const cmd = [
|
||||
'certbot',
|
||||
'certonly',
|
||||
'--webroot',
|
||||
'-w',
|
||||
WEBROOT_PATH_IN_CERTBOT + '/' + domainName,
|
||||
'-d',
|
||||
domainName,
|
||||
];
|
||||
if (shouldUseStaging) {
|
||||
cmd.push('--staging');
|
||||
}
|
||||
return self.runCommand(cmd).then(function (output) {
|
||||
Logger.d(output);
|
||||
if (output.indexOf('Congratulations! Your certificate and chain have been saved') >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (output.indexOf('Certificate not yet due for renewal; no action taken') >= 0) {
|
||||
return true;
|
||||
}
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.VERIFICATION_FAILED, 'Unexpected output when enabling SSL for' +
|
||||
domainName +
|
||||
' with ACME Certbot \n' +
|
||||
output);
|
||||
});
|
||||
});
|
||||
}
|
||||
ensureRegistered(emailAddress) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
// Creds used to be saved at
|
||||
// /etc/letencrypt/accounts/acme-v01.api.letsencrypt.org/directory/9fc95dbca2f0b877
|
||||
// After moving to 0.29.1, Certbot started using v2 API. and this path is no longer valid.
|
||||
// Instead, they use v02 path. However, old installations who registered with v1, will remain in the same directory
|
||||
const cmd = [
|
||||
'certbot',
|
||||
'register',
|
||||
'--email',
|
||||
emailAddress,
|
||||
'--agree-tos',
|
||||
'--no-eff-email',
|
||||
];
|
||||
if (shouldUseStaging) {
|
||||
cmd.push('--staging');
|
||||
}
|
||||
return self.runCommand(cmd);
|
||||
})
|
||||
.then(function (registerOutput) {
|
||||
if (registerOutput.indexOf('Your account credentials have been saved in your Certbot') >= 0) {
|
||||
return true;
|
||||
}
|
||||
if (registerOutput.indexOf('There is an existing account') >= 0) {
|
||||
return true;
|
||||
}
|
||||
throw new Error('Unexpected output when registering with ACME Certbot \n' +
|
||||
registerOutput);
|
||||
});
|
||||
}
|
||||
/*
|
||||
Certificate Name: customdomain-another.hm2.caprover.com
|
||||
Domains: customdomain-another.hm2.caprover.com
|
||||
Expiry Date: 2019-03-22 04:22:55+00:00 (VALID: 81 days)
|
||||
Certificate Path: /etc/letsencrypt/live/customdomain-another.hm2.caprover.com/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/customdomain-another.hm2.caprover.com/privkey.pem
|
||||
Certificate Name: testing.cp.hm.caprover.com
|
||||
Domains: testing.cp.hm.caprover.com
|
||||
Expiry Date: 2019-03-21 18:42:17+00:00 (VALID: 81 days)
|
||||
Certificate Path: /etc/letsencrypt/live/testing.cp.hm.caprover.com/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/testing.cp.hm.caprover.com/privkey.pem
|
||||
Certificate Name: registry.cp.hm.caprover.com
|
||||
Domains: registry.cp.hm.caprover.com
|
||||
Expiry Date: 2019-03-25 04:56:45+00:00 (VALID: 84 days)
|
||||
Certificate Path: /etc/letsencrypt/live/registry.cp.hm.caprover.com/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/registry.cp.hm.caprover.com/privkey.pem
|
||||
Certificate Name: captain.cp.hm.caprover.com
|
||||
Domains: captain.cp.hm.caprover.com
|
||||
Expiry Date: 2019-03-20 22:25:50+00:00 (VALID: 80 days)
|
||||
Certificate Path: /etc/letsencrypt/live/captain.cp.hm.caprover.com/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/captain.cp.hm.caprover.com/privkey.pem
|
||||
Certificate Name: testing2.cp.hm.caprover.com
|
||||
Domains: testing2.cp.hm.caprover.com
|
||||
Expiry Date: 2019-03-21 18:42:55+00:00 (VALID: 81 days)
|
||||
Certificate Path: /etc/letsencrypt/live/testing2.cp.hm.caprover.com/fullchain.pem
|
||||
Private Key Path: /etc/letsencrypt/live/testing2.cp.hm.caprover.com/privkey.pem
|
||||
|
||||
*/
|
||||
ensureAllCurrentlyRegisteredDomainsHaveDirs() {
|
||||
const self = this;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return self
|
||||
.runCommand(['certbot', 'certificates'])
|
||||
.then(function (output) {
|
||||
const lines = output.split('\n');
|
||||
const domains = [];
|
||||
lines.forEach(l => {
|
||||
if (l.indexOf('Certificate Name:') >= 0) {
|
||||
domains.push(l.replace('Certificate Name:', '').trim());
|
||||
}
|
||||
});
|
||||
return domains;
|
||||
});
|
||||
})
|
||||
.then(function (allDomains) {
|
||||
const p = Promise.resolve();
|
||||
allDomains.forEach(d => {
|
||||
p.then(function () {
|
||||
return self.ensureDomainHasDirectory(d);
|
||||
});
|
||||
});
|
||||
return p;
|
||||
});
|
||||
}
|
||||
runCommand(cmd) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const self = this;
|
||||
return Promise.resolve().then(function () {
|
||||
const nonInterActiveCommand = [...cmd, '--non-interactive'];
|
||||
return dockerApi.executeCommand(CaptainConstants.certbotServiceName, nonInterActiveCommand);
|
||||
});
|
||||
}
|
||||
ensureDomainHasDirectory(domainName) {
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return fs.ensureDir(WEBROOT_PATH_IN_CAPTAIN + '/' + domainName);
|
||||
});
|
||||
}
|
||||
renewAllCerts() {
|
||||
const self = this;
|
||||
/*
|
||||
From Certbot docs:
|
||||
This command attempts to renew all previously-obtained certificates that expire in less than 30 days.
|
||||
The same plugin and options that were used at the time the certificate was originally issued will be
|
||||
used for the renewal attempt, unless you specify other plugins or options. Unlike certonly, renew
|
||||
acts on multiple certificates and always takes into account whether each one is near expiry. Because
|
||||
of this, renew is suitable (and designed) for automated use, to allow your system to automatically
|
||||
renew each certificate when appropriate. Since renew only renews certificates that are near expiry
|
||||
it can be run as frequently as you want - since it will usually take no action.
|
||||
*/
|
||||
// before doing renewal, let's schedule the next one in 20.3 hours!
|
||||
// this random schedule helps to avoid retrying at the same time of
|
||||
// the day in case if that's our super high traffic time
|
||||
setTimeout(function () {
|
||||
self.renewAllCerts();
|
||||
}, 1000 * 3600 * 20.3);
|
||||
const cmd = ['certbot', 'renew'];
|
||||
if (shouldUseStaging) {
|
||||
cmd.push('--staging');
|
||||
}
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return self.ensureAllCurrentlyRegisteredDomainsHaveDirs();
|
||||
})
|
||||
.then(function () {
|
||||
return self.runCommand(cmd);
|
||||
})
|
||||
.then(function (output) {
|
||||
// Ignore output :)
|
||||
});
|
||||
}
|
||||
init(myNodeId) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const self = this;
|
||||
function createCertbotServiceOnNode(nodeId) {
|
||||
return dockerApi
|
||||
.createServiceOnNodeId(CaptainConstants.certbotImageName, CaptainConstants.certbotServiceName, undefined, nodeId, undefined, undefined, undefined)
|
||||
.then(function () {
|
||||
const waitTimeInMillis = 5000;
|
||||
Logger.d('Waiting for ' +
|
||||
waitTimeInMillis / 1000 +
|
||||
' seconds for Certbot to start up');
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
resolve(true);
|
||||
}, waitTimeInMillis);
|
||||
});
|
||||
});
|
||||
}
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.letsEncryptEtcPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.letsEncryptLibPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(WEBROOT_PATH_IN_CAPTAIN);
|
||||
})
|
||||
.then(function () {
|
||||
return dockerApi.isServiceRunningByName(CaptainConstants.certbotServiceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
if (isRunning) {
|
||||
Logger.d('Captain Certbot is already running.. ');
|
||||
return dockerApi.getNodeIdByServiceName(CaptainConstants.certbotServiceName, 0);
|
||||
}
|
||||
else {
|
||||
Logger.d('No Captain Certbot service is running. Creating one...');
|
||||
return createCertbotServiceOnNode(myNodeId).then(function () {
|
||||
return myNodeId;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function (nodeId) {
|
||||
if (nodeId !== myNodeId) {
|
||||
Logger.d('Captain Certbot is running on a different node. Removing...');
|
||||
return dockerApi
|
||||
.removeServiceByName(CaptainConstants.certbotServiceName)
|
||||
.then(function () {
|
||||
return createCertbotServiceOnNode(myNodeId).then(function () {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Updating Certbot service...');
|
||||
return dockerApi.updateService(CaptainConstants.certbotServiceName, CaptainConstants.certbotImageName, [
|
||||
{
|
||||
hostPath: CaptainConstants.letsEncryptEtcPath,
|
||||
containerPath: '/etc/letsencrypt',
|
||||
},
|
||||
{
|
||||
hostPath: CaptainConstants.letsEncryptLibPath,
|
||||
containerPath: '/var/lib/letsencrypt',
|
||||
},
|
||||
{
|
||||
hostPath: WEBROOT_PATH_IN_CAPTAIN,
|
||||
containerPath: WEBROOT_PATH_IN_CERTBOT,
|
||||
},
|
||||
],
|
||||
// No need to certbot to be connected to the network
|
||||
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
})
|
||||
.then(function () {
|
||||
return self.ensureAllCurrentlyRegisteredDomainsHaveDirs();
|
||||
})
|
||||
.then(function () {
|
||||
// schedule the first attempt to renew certs in 1 minute
|
||||
setTimeout(function () {
|
||||
self.renewAllCerts();
|
||||
}, 1000 * 60);
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = CertbotManager;
|
||||
//# sourceMappingURL=CertbotManager.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,416 +0,0 @@
|
||||
"use strict";
|
||||
const ejs = require("ejs");
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const fs = require("fs-extra");
|
||||
const uuid = require("uuid/v4");
|
||||
const request = require("request");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const LoadBalancerInfo_1 = require("../../models/LoadBalancerInfo");
|
||||
const defaultPageTemplate = fs
|
||||
.readFileSync(__dirname + '/../../../template/default-page.ejs')
|
||||
.toString();
|
||||
class LoadBalancerManager {
|
||||
constructor(dockerApi, certbotManager, dataStore) {
|
||||
this.dockerApi = dockerApi;
|
||||
this.certbotManager = certbotManager;
|
||||
this.dataStore = dataStore;
|
||||
this.reloadInProcess = false;
|
||||
this.requestedReloadPromises = [];
|
||||
this.captainPublicRandomKey = uuid();
|
||||
}
|
||||
/**
|
||||
* Reloads the configuation for NGINX.
|
||||
* NOTE that this can return synchronously with UNDEFINED if there is already a process in the background.
|
||||
* @param dataStoreToQueue
|
||||
* @returns {Promise.<>}
|
||||
*/
|
||||
rePopulateNginxConfigFile(dataStoreToQueue) {
|
||||
const self = this;
|
||||
return new Promise(function (res, rej) {
|
||||
self.requestedReloadPromises.push({
|
||||
dataStore: dataStoreToQueue,
|
||||
resolve: res,
|
||||
reject: rej,
|
||||
});
|
||||
self.consumeQueueIfAnyInNginxReloadQueue();
|
||||
});
|
||||
}
|
||||
consumeQueueIfAnyInNginxReloadQueue() {
|
||||
const self = this;
|
||||
const q = self.requestedReloadPromises.pop();
|
||||
if (!q) {
|
||||
return;
|
||||
}
|
||||
if (self.reloadInProcess) {
|
||||
Logger.d('NGINX Reload already in process, Bouncing off...');
|
||||
return;
|
||||
}
|
||||
Logger.d('Locking NGINX configuration reloading...');
|
||||
self.reloadInProcess = true;
|
||||
const dataStore = q.dataStore;
|
||||
// This will resolve to something like: /captain/nginx/conf.d/captain
|
||||
const configFilePathBase = CaptainConstants.perAppNginxConfigPathBase +
|
||||
'/' +
|
||||
dataStore.getNameSpace();
|
||||
const FUTURE = configFilePathBase + '.fut';
|
||||
const BACKUP = configFilePathBase + '.bak';
|
||||
const CONFIG = configFilePathBase + '.conf';
|
||||
let nginxConfigContent = '';
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return fs.remove(FUTURE);
|
||||
})
|
||||
.then(function () {
|
||||
return dataStore.getServerList();
|
||||
})
|
||||
.then(function (servers) {
|
||||
const promises = [];
|
||||
if (servers && !!servers.length) {
|
||||
for (let i = 0; i < servers.length; i++) {
|
||||
const s = servers[i];
|
||||
if (s.hasSsl) {
|
||||
s.crtPath = self.getSslCertPath(s.publicDomain);
|
||||
s.keyPath = self.getSslKeyPath(s.publicDomain);
|
||||
}
|
||||
s.staticWebRoot =
|
||||
CaptainConstants.nginxStaticRootDir +
|
||||
CaptainConstants.nginxDomainSpecificHtmlDir +
|
||||
'/' +
|
||||
s.publicDomain;
|
||||
s.customErrorPagesDirectory =
|
||||
CaptainConstants.nginxStaticRootDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir;
|
||||
promises.push(Promise.resolve()
|
||||
.then(function () {
|
||||
return ejs.render(s.nginxConfigTemplate, {
|
||||
s: s,
|
||||
});
|
||||
})
|
||||
.then(function (rendered) {
|
||||
nginxConfigContent += rendered;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.outputFile(FUTURE, nginxConfigContent);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.remove(BACKUP);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureFile(CONFIG);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.renameSync(CONFIG, BACKUP); // sync method. It's really fast.
|
||||
})
|
||||
.then(function () {
|
||||
return fs.renameSync(FUTURE, CONFIG); // sync method. It's really fast.
|
||||
})
|
||||
.then(function () {
|
||||
return self.createRootConfFile(dataStore);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('SUCCESS: UNLocking NGINX configuration reloading...');
|
||||
self.reloadInProcess = false;
|
||||
q.resolve();
|
||||
self.consumeQueueIfAnyInNginxReloadQueue();
|
||||
})
|
||||
.catch(function (error) {
|
||||
Logger.e(error);
|
||||
Logger.d('Error: UNLocking NGINX configuration reloading...');
|
||||
self.reloadInProcess = false;
|
||||
q.reject(error);
|
||||
self.consumeQueueIfAnyInNginxReloadQueue();
|
||||
});
|
||||
}
|
||||
sendReloadSignal() {
|
||||
return this.dockerApi.sendSingleContainerKillHUP(CaptainConstants.nginxServiceName);
|
||||
}
|
||||
getCaptainPublicRandomKey() {
|
||||
return this.captainPublicRandomKey;
|
||||
}
|
||||
getSslCertPath(domainName) {
|
||||
const self = this;
|
||||
return (CaptainConstants.letsEncryptEtcPathOnNginx +
|
||||
self.certbotManager.getCertRelativePathForDomain(domainName));
|
||||
}
|
||||
getSslKeyPath(domainName) {
|
||||
const self = this;
|
||||
return (CaptainConstants.letsEncryptEtcPathOnNginx +
|
||||
self.certbotManager.getKeyRelativePathForDomain(domainName));
|
||||
}
|
||||
getInfo() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const url = 'http://' + CaptainConstants.nginxServiceName + '/nginx_status';
|
||||
request(url, function (error, response, body) {
|
||||
if (error || !body) {
|
||||
Logger.e('Error ' + error);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Request to nginx Failed.'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = new LoadBalancerInfo_1.default();
|
||||
const lines = body.split('\n');
|
||||
data.activeConnections = Number(lines[0].split(' ')[2].trim());
|
||||
data.accepted = Number(lines[2].split(' ')[1].trim());
|
||||
data.handled = Number(lines[2].split(' ')[2].trim());
|
||||
data.total = Number(lines[2].split(' ')[3].trim());
|
||||
data.reading = Number(lines[3].split(' ')[1].trim());
|
||||
data.writing = Number(lines[3].split(' ')[3].trim());
|
||||
data.waiting = Number(lines[3].split(' ')[5].trim());
|
||||
resolve(data);
|
||||
}
|
||||
catch (error) {
|
||||
Logger.e(error);
|
||||
reject(ApiStatusCodes.createError(ApiStatusCodes.STATUS_ERROR_GENERIC, 'Parser Failed. See internal logs...'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
createRootConfFile(dataStore) {
|
||||
const self = this;
|
||||
const captainDomain = CaptainConstants.captainSubDomain + '.' + dataStore.getRootDomain();
|
||||
const registryDomain = CaptainConstants.registrySubDomain + '.' + dataStore.getRootDomain();
|
||||
let hasRootSsl = false;
|
||||
const FUTURE = CaptainConstants.rootNginxConfigPath + '.fut';
|
||||
const BACKUP = CaptainConstants.rootNginxConfigPath + '.bak';
|
||||
const CONFIG = CaptainConstants.rootNginxConfigPath + '.conf';
|
||||
let rootNginxTemplate = undefined;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return dataStore.getNginxConfig();
|
||||
})
|
||||
.then(function (nginxConfig) {
|
||||
rootNginxTemplate =
|
||||
nginxConfig.captainConfig.customValue ||
|
||||
nginxConfig.captainConfig.byDefault;
|
||||
return dataStore.getHasRootSsl();
|
||||
})
|
||||
.then(function (hasSsl) {
|
||||
hasRootSsl = hasSsl;
|
||||
return dataStore.getHasRegistrySsl();
|
||||
})
|
||||
.then(function (hasRegistrySsl) {
|
||||
return ejs.render(rootNginxTemplate, {
|
||||
captain: {
|
||||
crtPath: self.getSslCertPath(captainDomain),
|
||||
keyPath: self.getSslKeyPath(captainDomain),
|
||||
hasRootSsl: hasRootSsl,
|
||||
serviceName: CaptainConstants.captainServiceName,
|
||||
domain: captainDomain,
|
||||
serviceExposedPort: CaptainConstants.captainServiceExposedPort,
|
||||
defaultHtmlDir: CaptainConstants.nginxStaticRootDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir,
|
||||
staticWebRoot: CaptainConstants.nginxStaticRootDir +
|
||||
CaptainConstants.nginxDomainSpecificHtmlDir +
|
||||
'/' +
|
||||
captainDomain,
|
||||
},
|
||||
registry: {
|
||||
crtPath: self.getSslCertPath(registryDomain),
|
||||
keyPath: self.getSslKeyPath(registryDomain),
|
||||
hasRootSsl: hasRegistrySsl,
|
||||
domain: registryDomain,
|
||||
staticWebRoot: CaptainConstants.nginxStaticRootDir +
|
||||
CaptainConstants.nginxDomainSpecificHtmlDir +
|
||||
'/' +
|
||||
registryDomain,
|
||||
},
|
||||
});
|
||||
})
|
||||
.then(function (rootNginxConfContent) {
|
||||
return fs.outputFile(FUTURE, rootNginxConfContent);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.remove(BACKUP);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureFile(CONFIG);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.renameSync(CONFIG, BACKUP); // sync method. It's really fast.
|
||||
})
|
||||
.then(function () {
|
||||
return fs.renameSync(FUTURE, CONFIG); // sync method. It's really fast.
|
||||
});
|
||||
}
|
||||
ensureBaseNginxConf() {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getNginxConfig();
|
||||
})
|
||||
.then(function (captainConfig) {
|
||||
const baseConfigTemplate = captainConfig.baseConfig.customValue ||
|
||||
captainConfig.baseConfig.byDefault;
|
||||
return ejs.render(baseConfigTemplate, {});
|
||||
})
|
||||
.then(function (baseNginxConfFileContent) {
|
||||
return fs.outputFile(CaptainConstants.baseNginxConfigPath, baseNginxConfFileContent);
|
||||
});
|
||||
}
|
||||
init(myNodeId, dataStore) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const self = this;
|
||||
function createNginxServiceOnNode(nodeId) {
|
||||
Logger.d('No Captain Nginx service is running. Creating one on captain node...');
|
||||
return dockerApi
|
||||
.createServiceOnNodeId(CaptainConstants.nginxImageName, CaptainConstants.nginxServiceName, [
|
||||
{
|
||||
protocol: 'tcp',
|
||||
publishMode: 'host',
|
||||
containerPort: 80,
|
||||
hostPort: CaptainConstants.nginxPortNumber,
|
||||
},
|
||||
{
|
||||
protocol: 'tcp',
|
||||
publishMode: 'host',
|
||||
containerPort: 443,
|
||||
hostPort: 443,
|
||||
},
|
||||
], nodeId, undefined, undefined, {
|
||||
Reservation: {
|
||||
MemoryBytes: 30 * 1024 * 1024,
|
||||
},
|
||||
})
|
||||
.then(function () {
|
||||
const waitTimeInMillis = 5000;
|
||||
Logger.d('Waiting for ' +
|
||||
waitTimeInMillis / 1000 +
|
||||
' seconds for nginx to start up');
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
resolve(true);
|
||||
}, waitTimeInMillis);
|
||||
});
|
||||
});
|
||||
}
|
||||
return fs
|
||||
.outputFile(CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir +
|
||||
CaptainConstants.captainConfirmationPath, self.getCaptainPublicRandomKey())
|
||||
.then(function () {
|
||||
return ejs.render(defaultPageTemplate, {
|
||||
message_title: 'Nothing here yet :/',
|
||||
message_body: '',
|
||||
message_link: 'https://caprover.com/',
|
||||
message_link_title: 'Read Docs',
|
||||
});
|
||||
})
|
||||
.then(function (staticPageContent) {
|
||||
return fs.outputFile(CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir +
|
||||
'/index.html', staticPageContent);
|
||||
})
|
||||
.then(function () {
|
||||
return ejs.render(defaultPageTemplate, {
|
||||
message_title: 'An Error Occurred :/',
|
||||
message_body: '',
|
||||
message_link: 'https://caprover.com/',
|
||||
message_link_title: 'Read Docs',
|
||||
});
|
||||
})
|
||||
.then(function (errorGenericPageContent) {
|
||||
return fs.outputFile(CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir +
|
||||
'/error_generic_catch_all.html', errorGenericPageContent);
|
||||
})
|
||||
.then(function () {
|
||||
return ejs.render(defaultPageTemplate, {
|
||||
message_title: 'NGINX 502 Error :/',
|
||||
message_body: "If you are the developer, check your application's logs. See the link below for details",
|
||||
message_link: 'https://caprover.com/docs/troubleshooting.html#successful-deploy-but-502-bad-gateway-error',
|
||||
message_link_title: 'Docs - 502 Troubleshooting',
|
||||
});
|
||||
})
|
||||
.then(function (error502PageContent) {
|
||||
return fs.outputFile(CaptainConstants.captainStaticFilesDir +
|
||||
CaptainConstants.nginxDefaultHtmlDir +
|
||||
'/captain_502_custom_error_page.html', error502PageContent);
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Setting up NGINX conf file...');
|
||||
return self.ensureBaseNginxConf();
|
||||
})
|
||||
.then(function () {
|
||||
return self.rePopulateNginxConfigFile(dataStore);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.letsEncryptEtcPath);
|
||||
})
|
||||
.then(function () {
|
||||
return fs.ensureDir(CaptainConstants.nginxSharedPathOnHost);
|
||||
})
|
||||
.then(function () {
|
||||
return dockerApi.isServiceRunningByName(CaptainConstants.nginxServiceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
if (isRunning) {
|
||||
Logger.d('Captain Nginx is already running.. ');
|
||||
return dockerApi.getNodeIdByServiceName(CaptainConstants.nginxServiceName, 0);
|
||||
}
|
||||
else {
|
||||
return createNginxServiceOnNode(myNodeId).then(function () {
|
||||
return myNodeId;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function (nodeId) {
|
||||
if (nodeId !== myNodeId) {
|
||||
Logger.d('Captain Nginx is running on a different node. Removing...');
|
||||
return dockerApi
|
||||
.removeServiceByName(CaptainConstants.nginxServiceName)
|
||||
.then(function () {
|
||||
return createNginxServiceOnNode(myNodeId).then(function () {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Updating NGINX service...');
|
||||
return dockerApi.updateService(CaptainConstants.nginxServiceName, CaptainConstants.nginxImageName, [
|
||||
{
|
||||
containerPath: CaptainConstants.nginxStaticRootDir,
|
||||
hostPath: CaptainConstants.captainStaticFilesDir,
|
||||
},
|
||||
{
|
||||
containerPath: '/etc/nginx/nginx.conf',
|
||||
hostPath: CaptainConstants.baseNginxConfigPath,
|
||||
},
|
||||
{
|
||||
containerPath: '/etc/nginx/conf.d',
|
||||
hostPath: CaptainConstants.perAppNginxConfigPathBase,
|
||||
},
|
||||
{
|
||||
containerPath: CaptainConstants.letsEncryptEtcPathOnNginx,
|
||||
hostPath: CaptainConstants.letsEncryptEtcPath,
|
||||
},
|
||||
{
|
||||
containerPath: CaptainConstants.nginxSharedPathOnNginx,
|
||||
hostPath: CaptainConstants.nginxSharedPathOnHost,
|
||||
},
|
||||
], [CaptainConstants.captainNetworkName], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
})
|
||||
.then(function () {
|
||||
const waitTimeInMillis = 5000;
|
||||
Logger.d('Waiting for ' +
|
||||
waitTimeInMillis / 1000 +
|
||||
' seconds for nginx reload to take into effect');
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
Logger.d('NGINX is fully set up and working...');
|
||||
resolve(true);
|
||||
}, waitTimeInMillis);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = LoadBalancerManager;
|
||||
//# sourceMappingURL=LoadBalancerManager.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,174 +0,0 @@
|
||||
"use strict";
|
||||
const CaptainConstants = require("../../utils/CaptainConstants");
|
||||
const Logger = require("../../utils/Logger");
|
||||
const fs = require("fs-extra");
|
||||
const ApiStatusCodes = require("../../api/ApiStatusCodes");
|
||||
const bcrypt = require("bcryptjs");
|
||||
class SelfHostedDockerRegistry {
|
||||
constructor(dockerApi, dataStore, certbotManager, loadBalancerManager, myNodeId) {
|
||||
this.dockerApi = dockerApi;
|
||||
this.dataStore = dataStore;
|
||||
this.certbotManager = certbotManager;
|
||||
this.loadBalancerManager = loadBalancerManager;
|
||||
this.myNodeId = myNodeId;
|
||||
//
|
||||
}
|
||||
enableRegistrySsl() {
|
||||
const self = this;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return self.dataStore.getHasRootSsl();
|
||||
})
|
||||
.then(function (rootHasSsl) {
|
||||
if (!rootHasSsl) {
|
||||
throw ApiStatusCodes.createError(ApiStatusCodes.ILLEGAL_OPERATION, 'Root must have SSL before enabling ssl for docker registry.');
|
||||
}
|
||||
return self.certbotManager.enableSsl(CaptainConstants.registrySubDomain +
|
||||
'.' +
|
||||
self.dataStore.getRootDomain());
|
||||
})
|
||||
.then(function () {
|
||||
return self.dataStore.setHasRegistrySsl(true);
|
||||
})
|
||||
.then(function () {
|
||||
return self.loadBalancerManager.rePopulateNginxConfigFile(self.dataStore);
|
||||
})
|
||||
.then(function () {
|
||||
return self.loadBalancerManager.sendReloadSignal();
|
||||
});
|
||||
}
|
||||
getLocalRegistryDomainAndPort() {
|
||||
const self = this;
|
||||
return (CaptainConstants.registrySubDomain +
|
||||
'.' +
|
||||
self.dataStore.getRootDomain() +
|
||||
':' +
|
||||
CaptainConstants.configs.registrySubDomainPort);
|
||||
}
|
||||
ensureServiceRemoved() {
|
||||
const dockerApi = this.dockerApi;
|
||||
return Promise.resolve() //
|
||||
.then(function () {
|
||||
return dockerApi.isServiceRunningByName(CaptainConstants.registryServiceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
if (!isRunning)
|
||||
return;
|
||||
return dockerApi.removeServiceByName(CaptainConstants.registryServiceName);
|
||||
});
|
||||
}
|
||||
ensureDockerRegistryRunningOnThisNode(password) {
|
||||
const dockerApi = this.dockerApi;
|
||||
const dataStore = this.dataStore;
|
||||
function createRegistryServiceOnNode(nodeId) {
|
||||
return dockerApi
|
||||
.createServiceOnNodeId(CaptainConstants.registryImageName, CaptainConstants.registryServiceName, undefined, nodeId, undefined, undefined, undefined)
|
||||
.then(function () {
|
||||
const waitTimeInMillis = 5000;
|
||||
Logger.d('Waiting for ' +
|
||||
waitTimeInMillis / 1000 +
|
||||
' seconds for Registry to start up');
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
resolve(true);
|
||||
}, waitTimeInMillis);
|
||||
});
|
||||
});
|
||||
}
|
||||
const myNodeId = this.myNodeId;
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
const authContent = CaptainConstants.captainRegistryUsername +
|
||||
':' +
|
||||
bcrypt.hashSync(password, bcrypt.genSaltSync(5));
|
||||
return fs.outputFile(CaptainConstants.registryAuthPathOnHost, authContent);
|
||||
})
|
||||
.then(function () {
|
||||
return dockerApi.isServiceRunningByName(CaptainConstants.registryServiceName);
|
||||
})
|
||||
.then(function (isRunning) {
|
||||
if (isRunning) {
|
||||
Logger.d('Captain Registry is already running.. ');
|
||||
return dockerApi.getNodeIdByServiceName(CaptainConstants.registryServiceName, 0);
|
||||
}
|
||||
else {
|
||||
Logger.d('No Captain Registry service is running. Creating one...');
|
||||
return createRegistryServiceOnNode(myNodeId).then(function () {
|
||||
return myNodeId;
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function (nodeId) {
|
||||
if (nodeId !== myNodeId) {
|
||||
Logger.d('Captain Registry is running on a different node. Removing...');
|
||||
return dockerApi
|
||||
.removeServiceByName(CaptainConstants.registryServiceName)
|
||||
.then(function () {
|
||||
Logger.d('Creating Registry on this node...');
|
||||
return createRegistryServiceOnNode(myNodeId).then(function () {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
Logger.d('Updating Certbot service...');
|
||||
return dockerApi.updateService(CaptainConstants.registryServiceName, CaptainConstants.registryImageName, [
|
||||
{
|
||||
containerPath: '/cert-files',
|
||||
hostPath: CaptainConstants.letsEncryptEtcPath,
|
||||
},
|
||||
{
|
||||
containerPath: '/var/lib/registry',
|
||||
hostPath: CaptainConstants.registryPathOnHost,
|
||||
},
|
||||
{
|
||||
containerPath: '/etc/auth',
|
||||
hostPath: CaptainConstants.registryAuthPathOnHost,
|
||||
},
|
||||
],
|
||||
// No need for registry to be connected to the network
|
||||
undefined, [
|
||||
{
|
||||
key: 'REGISTRY_HTTP_TLS_CERTIFICATE',
|
||||
value: '/cert-files/live/' +
|
||||
CaptainConstants.registrySubDomain +
|
||||
'.' +
|
||||
dataStore.getRootDomain() +
|
||||
'/fullchain.pem',
|
||||
},
|
||||
{
|
||||
key: 'REGISTRY_HTTP_TLS_KEY',
|
||||
value: '/cert-files/live/' +
|
||||
CaptainConstants.registrySubDomain +
|
||||
'.' +
|
||||
dataStore.getRootDomain() +
|
||||
'/privkey.pem',
|
||||
},
|
||||
{
|
||||
key: 'REGISTRY_AUTH',
|
||||
value: 'htpasswd',
|
||||
},
|
||||
{
|
||||
key: 'REGISTRY_AUTH_HTPASSWD_REALM',
|
||||
value: 'Registry Realm',
|
||||
},
|
||||
{
|
||||
key: 'REGISTRY_AUTH_HTPASSWD_PATH',
|
||||
value: '/etc/auth',
|
||||
},
|
||||
], undefined, undefined, undefined, undefined, undefined, [
|
||||
{
|
||||
protocol: 'tcp',
|
||||
containerPort: 5000,
|
||||
hostPort: CaptainConstants.configs.registrySubDomainPort,
|
||||
},
|
||||
], undefined, undefined, undefined);
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = SelfHostedDockerRegistry;
|
||||
//# sourceMappingURL=SelfHostedDockerRegistry.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SelfHostedDockerRegistry.js","sourceRoot":"","sources":["../../../src/user/system/SelfHostedDockerRegistry.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,100 +0,0 @@
|
||||
"use strict";
|
||||
const fs = require("fs-extra");
|
||||
const EnvVars = require("./EnvVars");
|
||||
const CAPTAIN_BASE_DIRECTORY = '/captain';
|
||||
const CAPTAIN_DATA_DIRECTORY = CAPTAIN_BASE_DIRECTORY + '/data'; // data that sits here can be backed up
|
||||
const CAPTAIN_ROOT_DIRECTORY_TEMP = CAPTAIN_BASE_DIRECTORY + '/temp';
|
||||
const CAPTAIN_ROOT_DIRECTORY_GENERATED = CAPTAIN_BASE_DIRECTORY + '/generated';
|
||||
const CONSTANT_FILE_OVERRIDE = CAPTAIN_DATA_DIRECTORY + '/override.json';
|
||||
const configs = {
|
||||
publishedNameOnDockerHub: 'caprover/caprover',
|
||||
defaultMaxLogSize: '512m',
|
||||
buildLogSize: 50,
|
||||
preCheckForWildCard: true,
|
||||
registrySubDomainPort: 996,
|
||||
};
|
||||
let data = {
|
||||
configs: configs,
|
||||
// ******************** Global Constants *********************
|
||||
apiVersion: 'v2',
|
||||
version: '0.7.3',
|
||||
isDebug: EnvVars.CAPTAIN_IS_DEBUG,
|
||||
captainServiceExposedPort: 3000,
|
||||
rootNameSpace: 'captain',
|
||||
// *********************** Disk Paths ************************
|
||||
dockerSocketPath: '/var/run/docker.sock',
|
||||
sourcePathInContainer: '/usr/src/app',
|
||||
nginxStaticRootDir: '/usr/share/nginx',
|
||||
captainStaticFilesDir: CAPTAIN_ROOT_DIRECTORY_GENERATED + '/static',
|
||||
nginxSharedPathOnNginx: '/nginx-shared',
|
||||
nginxDefaultHtmlDir: '/default',
|
||||
letsEncryptEtcPathOnNginx: '/letencrypt/etc',
|
||||
nginxDomainSpecificHtmlDir: '/domains',
|
||||
captainConfirmationPath: '/.well-known/captain-identifier',
|
||||
captainBaseDirectory: CAPTAIN_BASE_DIRECTORY,
|
||||
captainRootDirectoryTemp: CAPTAIN_ROOT_DIRECTORY_TEMP,
|
||||
captainRawSourceDirectoryBase: CAPTAIN_ROOT_DIRECTORY_TEMP + '/image_raw',
|
||||
captainRootDirectoryGenerated: CAPTAIN_ROOT_DIRECTORY_GENERATED,
|
||||
registryAuthPathOnHost: CAPTAIN_ROOT_DIRECTORY_GENERATED + '/registry-auth',
|
||||
baseNginxConfigPath: CAPTAIN_ROOT_DIRECTORY_GENERATED + '/nginx/nginx.conf',
|
||||
rootNginxConfigPath: CAPTAIN_ROOT_DIRECTORY_GENERATED + '/nginx/conf.d/captain-root',
|
||||
perAppNginxConfigPathBase: CAPTAIN_ROOT_DIRECTORY_GENERATED + '/nginx/conf.d',
|
||||
captainDataDirectory: CAPTAIN_DATA_DIRECTORY,
|
||||
letsEncryptLibPath: CAPTAIN_DATA_DIRECTORY + '/letencrypt/lib',
|
||||
letsEncryptEtcPath: CAPTAIN_DATA_DIRECTORY + '/letencrypt/etc',
|
||||
registryPathOnHost: CAPTAIN_DATA_DIRECTORY + '/registry',
|
||||
nginxSharedPathOnHost: CAPTAIN_DATA_DIRECTORY + '/nginx-shared',
|
||||
debugSourceDirectory: '',
|
||||
// **************** DockerHub Image Names ********************
|
||||
certbotImageName: 'dockersaturn/certbot-sleeping:v0.29.1',
|
||||
netDataImageName: 'titpetric/netdata:1.8',
|
||||
registryImageName: 'registry:2',
|
||||
appPlaceholderImageName: 'dockersaturn/app-placeholder:latest',
|
||||
nginxImageName: 'nginx:1',
|
||||
// ********************* Local Docker Constants ************************
|
||||
defaultEmail: 'runner@caprover.com',
|
||||
captainSaltSecretKey: 'captain-salt',
|
||||
nginxServiceName: 'captain-nginx',
|
||||
captainServiceName: 'captain-captain',
|
||||
certbotServiceName: 'captain-certbot',
|
||||
netDataContainerName: 'captain-netdata-container',
|
||||
registryServiceName: 'captain-registry',
|
||||
captainNetworkName: 'captain-overlay-network',
|
||||
captainRegistryUsername: 'captain',
|
||||
// ********************* HTTP Related Constants ************************
|
||||
nginxPortNumber: 80,
|
||||
netDataRelativePath: '/net-data-monitor',
|
||||
healthCheckEndPoint: '/checkhealth',
|
||||
captainSubDomain: 'captain',
|
||||
registrySubDomain: 'registry',
|
||||
headerCookieAuth: 'captainCookieAuth',
|
||||
headerAuth: 'x-captain-auth',
|
||||
headerNamespace: 'x-namespace',
|
||||
};
|
||||
let overridingValuesConfigs = fs.readJsonSync(CONSTANT_FILE_OVERRIDE, {
|
||||
throws: false,
|
||||
});
|
||||
if (!!overridingValuesConfigs) {
|
||||
for (let prop in overridingValuesConfigs) {
|
||||
if (!overridingValuesConfigs.hasOwnProperty(prop)) {
|
||||
continue;
|
||||
}
|
||||
console.log('Overriding ' + prop);
|
||||
// @ts-ignore
|
||||
configs[prop] = overridingValuesConfigs[prop];
|
||||
}
|
||||
}
|
||||
if (data.isDebug) {
|
||||
let devDirectoryOnLocalMachine = fs
|
||||
.readFileSync(__dirname + '/../../currentdirectory')
|
||||
.toString()
|
||||
.trim();
|
||||
if (!devDirectoryOnLocalMachine) {
|
||||
throw new Error('For development purposes, you need to assign your local directory here');
|
||||
}
|
||||
data.debugSourceDirectory = devDirectoryOnLocalMachine;
|
||||
data.configs.publishedNameOnDockerHub = 'captain-debug';
|
||||
data.nginxPortNumber = 80;
|
||||
}
|
||||
module.exports = data;
|
||||
//# sourceMappingURL=CaptainConstants.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CaptainConstants.js","sourceRoot":"","sources":["../../src/utils/CaptainConstants.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,273 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const externalIp = require("public-ip");
|
||||
const DockerApi_1 = require("../docker/DockerApi");
|
||||
const CaptainConstants = require("./CaptainConstants");
|
||||
const EnvVar = require("./EnvVars");
|
||||
const http = require("http");
|
||||
const request = require("request");
|
||||
// internal IP returns Public IP if the machine is not behind a NAT
|
||||
// No need to directly use Public IP.
|
||||
function checkSystemReq() {
|
||||
return Promise.resolve()
|
||||
.then(function () {
|
||||
return DockerApi_1.default.get().getDockerVersion();
|
||||
})
|
||||
.then(function (output) {
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log(' >>>> Checking System Compatibility <<<<');
|
||||
let ver = output.Version.split('.');
|
||||
let maj = Number(ver[0]);
|
||||
let min = Number(ver[1]);
|
||||
let versionOk = false;
|
||||
if (maj > 17) {
|
||||
versionOk = true;
|
||||
}
|
||||
else if (maj === 17 && min >= 6) {
|
||||
versionOk = true;
|
||||
}
|
||||
if (versionOk) {
|
||||
console.log(' Docker Version passed.');
|
||||
}
|
||||
else {
|
||||
console.log('Warning!! Minimum Docker version is 17.06.x CapRover may not run properly on your Docker version.');
|
||||
}
|
||||
return DockerApi_1.default.get().getDockerInfo();
|
||||
})
|
||||
.then(function (output) {
|
||||
if (output.OperatingSystem.toLowerCase().indexOf('ubuntu') < 0) {
|
||||
console.log('Warning!! CapRover and Docker work best on Ubuntu - specially when it comes to storage drivers.');
|
||||
}
|
||||
else {
|
||||
console.log(' Ubuntu detected.');
|
||||
}
|
||||
if (output.Architecture.toLowerCase().indexOf('x86') < 0) {
|
||||
console.log('Warning!! Default CapRover is compiled for X86 CPU. To use CapRover on other CPUs you can build from the source code');
|
||||
}
|
||||
else {
|
||||
console.log(' X86 CPU detected.');
|
||||
}
|
||||
let totalMemInMb = Math.round(output.MemTotal / 1000.0 / 1000.0);
|
||||
if (totalMemInMb < 1000) {
|
||||
console.log('Warning!! With less than 1GB RAM, complex Docker builds might fail, see CapRover system requirements.');
|
||||
}
|
||||
else {
|
||||
console.log(' Total RAM ' + totalMemInMb + ' MB');
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log('**** WARNING!!!! System requirement check failed! *****');
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
const FIREWALL_PASSED = 'firewall-passed';
|
||||
function startServerOnPort_80_443_3000() {
|
||||
return Promise.resolve().then(function () {
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
});
|
||||
res.write(FIREWALL_PASSED);
|
||||
res.end();
|
||||
}).listen(80);
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
});
|
||||
res.write(FIREWALL_PASSED);
|
||||
res.end();
|
||||
}).listen(443);
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
});
|
||||
res.write(FIREWALL_PASSED);
|
||||
res.end();
|
||||
}).listen(3000);
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve();
|
||||
}, 4000);
|
||||
});
|
||||
});
|
||||
}
|
||||
function checkPortOrThrow(ipAddr, portToTest) {
|
||||
if (CaptainConstants.isDebug || !!EnvVar.BY_PASS_PROXY_CHECK) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
function printError() {
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log('Your firewall may have been blocking an in-use port: ' + portToTest);
|
||||
console.log('A simple solution on Ubuntu systems is to run "ufw disable"');
|
||||
console.log('Or just allowing necessary ports:');
|
||||
console.log(' ufw allow 80,443,3000,996,7946,4789,2377/tcp');
|
||||
console.log(' ufw allow 7946,4789,2377/udp');
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
console.log('See docs for more details on how to fix firewall issues');
|
||||
console.log(' ');
|
||||
console.log('If you are an advanced user, and you want to bypass this check (NOT RECOMMENDED),');
|
||||
console.log("you can append the docker command with an addition flag: -e BY_PASS_PROXY_CHECK='TRUE'");
|
||||
console.log(' ');
|
||||
console.log(' ');
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
let finished = false;
|
||||
setTimeout(function () {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
finished = true;
|
||||
printError();
|
||||
reject(new Error('Port timed out: ' + portToTest));
|
||||
}, 5000);
|
||||
request('http://' + ipAddr + ':' + portToTest, function (error, response, body) {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
finished = true;
|
||||
if (body + '' === FIREWALL_PASSED) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
printError();
|
||||
reject(new Error('Port seems to be closed: ' + portToTest));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
let myIp4;
|
||||
function install() {
|
||||
Promise.resolve()
|
||||
.then(function () {
|
||||
return checkSystemReq();
|
||||
})
|
||||
.then(function () {
|
||||
if (EnvVar.MAIN_NODE_IP_ADDRESS) {
|
||||
return EnvVar.MAIN_NODE_IP_ADDRESS;
|
||||
}
|
||||
return externalIp.v4();
|
||||
})
|
||||
.then(function (ip4) {
|
||||
if (!ip4) {
|
||||
throw new Error('Something went wrong. No IP address was retrieved.');
|
||||
}
|
||||
if (CaptainConstants.isDebug) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
DockerApi_1.default.get()
|
||||
.swarmLeave(true)
|
||||
.then(function (ignore) {
|
||||
resolve(ip4);
|
||||
})
|
||||
.catch(function (error) {
|
||||
if (error && error.statusCode === 503) {
|
||||
resolve(ip4);
|
||||
}
|
||||
else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
return ip4;
|
||||
}
|
||||
})
|
||||
.then(function (ip4) {
|
||||
myIp4 = '' + ip4;
|
||||
return startServerOnPort_80_443_3000();
|
||||
})
|
||||
.then(function () {
|
||||
return checkPortOrThrow(myIp4, 80);
|
||||
})
|
||||
.then(function () {
|
||||
return checkPortOrThrow(myIp4, 443);
|
||||
})
|
||||
.then(function () {
|
||||
return checkPortOrThrow(myIp4, 3000);
|
||||
})
|
||||
.then(function () {
|
||||
return DockerApi_1.default.get().initSwarm(myIp4);
|
||||
})
|
||||
.then(function (swarmId) {
|
||||
console.log('Swarm started: ' + swarmId);
|
||||
return DockerApi_1.default.get().getLeaderNodeId();
|
||||
})
|
||||
.then(function (nodeId) {
|
||||
let volumeToMount = [
|
||||
{
|
||||
hostPath: CaptainConstants.captainBaseDirectory,
|
||||
containerPath: CaptainConstants.captainBaseDirectory,
|
||||
},
|
||||
];
|
||||
let env = [];
|
||||
env.push({
|
||||
key: EnvVar.keys.IS_CAPTAIN_INSTANCE,
|
||||
value: '1',
|
||||
});
|
||||
if (EnvVar.DEFAULT_PASSWORD) {
|
||||
env.push({
|
||||
key: EnvVar.keys.DEFAULT_PASSWORD,
|
||||
value: EnvVar.DEFAULT_PASSWORD,
|
||||
});
|
||||
}
|
||||
if (EnvVar.CAPTAIN_DOCKER_API) {
|
||||
env.push({
|
||||
key: EnvVar.keys.CAPTAIN_DOCKER_API,
|
||||
value: EnvVar.CAPTAIN_DOCKER_API,
|
||||
});
|
||||
}
|
||||
else {
|
||||
volumeToMount.push({
|
||||
hostPath: CaptainConstants.dockerSocketPath,
|
||||
containerPath: CaptainConstants.dockerSocketPath,
|
||||
});
|
||||
}
|
||||
let ports = [];
|
||||
let captainNameAndVersion = CaptainConstants.configs.publishedNameOnDockerHub +
|
||||
':' +
|
||||
CaptainConstants.version;
|
||||
if (CaptainConstants.isDebug) {
|
||||
captainNameAndVersion =
|
||||
CaptainConstants.configs.publishedNameOnDockerHub; // debug doesn't have version.
|
||||
env.push({
|
||||
key: EnvVar.keys.CAPTAIN_IS_DEBUG,
|
||||
value: EnvVar.CAPTAIN_IS_DEBUG + '',
|
||||
});
|
||||
volumeToMount.push({
|
||||
hostPath: CaptainConstants.debugSourceDirectory,
|
||||
containerPath: CaptainConstants.sourcePathInContainer,
|
||||
});
|
||||
ports.push({
|
||||
containerPort: 38000,
|
||||
hostPort: 38000,
|
||||
});
|
||||
}
|
||||
ports.push({
|
||||
protocol: 'tcp',
|
||||
containerPort: CaptainConstants.captainServiceExposedPort,
|
||||
hostPort: CaptainConstants.captainServiceExposedPort,
|
||||
});
|
||||
return DockerApi_1.default.get().createServiceOnNodeId(captainNameAndVersion, CaptainConstants.captainServiceName, ports, nodeId, volumeToMount, env, {
|
||||
Reservation: {
|
||||
MemoryBytes: 100 * 1024 * 1024,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('Installation failed.');
|
||||
console.error(error);
|
||||
})
|
||||
.then(function () {
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
exports.install = install;
|
||||
//# sourceMappingURL=CaptainInstaller.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,49 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Content of this file is mostly taken from https://gist.github.com/vlucas/2bd40f62d20c1d49237a109d491974eb
|
||||
*/
|
||||
const crypto = require("crypto");
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const IV_LENGTH = 16; // For AES, this is always 16
|
||||
class CaptainEncryptor {
|
||||
constructor(encryptionKey) {
|
||||
if (!encryptionKey || encryptionKey.length < 32) {
|
||||
throw new Error('Encryption Key too short!');
|
||||
}
|
||||
encryptionKey = crypto
|
||||
.createHash('sha256')
|
||||
.update(encryptionKey)
|
||||
.digest('hex');
|
||||
if (!encryptionKey || encryptionKey.length < 32) {
|
||||
throw new Error('Encryption Key too short after hashing!');
|
||||
}
|
||||
this.encryptionKey = encryptionKey.slice(0, 32);
|
||||
}
|
||||
encrypt(clearText) {
|
||||
const self = this;
|
||||
let iv = crypto.randomBytes(IV_LENGTH);
|
||||
let key = new Buffer(self.encryptionKey);
|
||||
let cipher = crypto.createCipheriv(algorithm, key, iv);
|
||||
let encrypted = cipher.update(clearText);
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
||||
}
|
||||
decrypt(text) {
|
||||
const self = this;
|
||||
text = text + '';
|
||||
let textParts = text.split(':');
|
||||
let shifted = textParts.shift();
|
||||
if (!shifted)
|
||||
throw new Error('text.split failed');
|
||||
let iv = new Buffer(shifted, 'hex');
|
||||
let encryptedText = new Buffer(textParts.join(':'), 'hex');
|
||||
let key = new Buffer(self.encryptionKey);
|
||||
let decipher = crypto.createDecipheriv(algorithm, key, iv);
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString();
|
||||
}
|
||||
}
|
||||
exports.CaptainEncryptor = CaptainEncryptor;
|
||||
//# sourceMappingURL=Encryptor.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Encryptor.js","sourceRoot":"","sources":["../../src/utils/Encryptor.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,iCAAiC;AAEjC,MAAM,SAAS,GAAG,aAAa,CAAA;AAC/B,MAAM,SAAS,GAAG,EAAE,CAAA,CAAC,6BAA6B;AAElD,MAAa,gBAAgB;IAGzB,YAAY,aAAqB;QAC7B,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,EAAE,EAAE;YAC7C,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;SAC/C;QAED,aAAa,GAAG,MAAM;aACjB,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,aAAa,CAAC;aACrB,MAAM,CAAC,KAAK,CAAC,CAAA;QAElB,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,EAAE,EAAE;YAC7C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;SAC7D;QAED,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACnD,CAAC;IAED,OAAO,CAAC,SAAiB;QACrB,MAAM,IAAI,GAAG,IAAI,CAAA;QAEjB,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;QACtC,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACxC,IAAI,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;QACtD,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAExC,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QAEtD,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/D,CAAC;IAED,OAAO,CAAC,IAAY;QAChB,MAAM,IAAI,GAAG,IAAI,CAAA;QACjB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAA;QAEhB,IAAI,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC/B,IAAI,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,CAAA;QAC/B,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;QAElD,IAAI,EAAE,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,IAAI,aAAa,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;QAC1D,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QACxC,IAAI,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;QAC1D,IAAI,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAE9C,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;QAExD,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAA;IAC/B,CAAC;CACJ;AAnDD,4CAmDC"}
|
||||
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
module.exports = {
|
||||
keys: {
|
||||
CAPTAIN_DOCKER_API: 'CAPTAIN_DOCKER_API',
|
||||
CAPTAIN_IS_DEBUG: 'CAPTAIN_IS_DEBUG',
|
||||
DEFAULT_PASSWORD: 'DEFAULT_PASSWORD',
|
||||
IS_CAPTAIN_INSTANCE: 'IS_CAPTAIN_INSTANCE',
|
||||
},
|
||||
BY_PASS_PROXY_CHECK: process.env.BY_PASS_PROXY_CHECK,
|
||||
CAPTAIN_DOCKER_API: process.env.CAPTAIN_DOCKER_API,
|
||||
CAPTAIN_IS_DEBUG: !!process.env.CAPTAIN_IS_DEBUG,
|
||||
MAIN_NODE_IP_ADDRESS: process.env.MAIN_NODE_IP_ADDRESS,
|
||||
IS_CAPTAIN_INSTANCE: process.env.IS_CAPTAIN_INSTANCE,
|
||||
DEFAULT_PASSWORD: process.env.DEFAULT_PASSWORD,
|
||||
};
|
||||
//# sourceMappingURL=EnvVars.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user