make sysinfo provider a setting
This commit is contained in:
+2
-2
@@ -38,7 +38,6 @@ var apps = require('./apps.js'),
|
||||
semver = require('semver'),
|
||||
settings = require('./settings.js'),
|
||||
superagent = require('superagent'),
|
||||
sysinfo = require('./sysinfo.js'),
|
||||
users = require('./users.js'),
|
||||
util = require('util');
|
||||
|
||||
@@ -246,12 +245,13 @@ function sendAliveStatus(callback) {
|
||||
appAutoupdatePattern: allSettings[settings.APP_AUTOUPDATE_PATTERN_KEY],
|
||||
boxAutoupdatePattern: allSettings[settings.BOX_AUTOUPDATE_PATTERN_KEY],
|
||||
timeZone: allSettings[settings.TIME_ZONE_KEY],
|
||||
sysinfoProvider: allSettings[settings.SYSINFO_CONFIG_KEY].provider
|
||||
};
|
||||
|
||||
var data = {
|
||||
version: constants.VERSION,
|
||||
adminFqdn: settings.adminFqdn(),
|
||||
provider: sysinfo.provider(),
|
||||
provider: settings.provider(),
|
||||
backendSettings: backendSettings,
|
||||
machine: {
|
||||
cpus: os.cpus(),
|
||||
|
||||
+3
-3
@@ -350,7 +350,7 @@ function registerSubdomains(app, overwrite, callback) {
|
||||
assert.strictEqual(typeof overwrite, 'boolean');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
const allDomains = [ { subdomain: app.location, domain: app.domain }].concat(app.alternateDomains);
|
||||
@@ -396,7 +396,7 @@ function unregisterSubdomains(app, allDomains, callback) {
|
||||
assert(Array.isArray(allDomains));
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
async.eachSeries(allDomains, function (domain, iteratorDone) {
|
||||
@@ -430,7 +430,7 @@ function waitForDnsPropagation(app, callback) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(new BoxError(BoxError.NETWORK_ERROR, `Error getting public IP: ${error.message}`));
|
||||
|
||||
domains.waitForDnsRecord(app.location, app.domain, 'A', ip, { interval: 5000, times: 240 }, function (error) {
|
||||
|
||||
+1
-2
@@ -47,7 +47,6 @@ var apps = require('./apps.js'),
|
||||
shell = require('./shell.js'),
|
||||
spawn = require('child_process').spawn,
|
||||
split = require('split'),
|
||||
sysinfo = require('./sysinfo.js'),
|
||||
tasks = require('./tasks.js'),
|
||||
users = require('./users.js');
|
||||
|
||||
@@ -136,7 +135,7 @@ function getConfig(callback) {
|
||||
version: constants.VERSION,
|
||||
isDemo: settings.isDemo(),
|
||||
memory: os.totalmem(),
|
||||
provider: sysinfo.provider(),
|
||||
provider: settings.provider(),
|
||||
cloudronName: allSettings[settings.CLOUDRON_NAME_KEY],
|
||||
uiSpec: custom.uiSpec()
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ function getQuery(dnsConfig, callback) {
|
||||
assert.strictEqual(typeof dnsConfig, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
callback(null, {
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ function verifyDnsConfig(domainObject, callback) {
|
||||
if (error && error.code === 'ENOTFOUND') return callback(new BoxError(BoxError.BAD_FIELD, `Unable to resolve ${fqdn}`, { field: 'nameservers' }));
|
||||
if (error || !result) return callback(new BoxError(BoxError.BAD_FIELD, error ? error.message : `Unable to resolve ${fqdn}`, { field: 'nameservers' }));
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Failed to detect IP of this server: ${error.message}`));
|
||||
|
||||
if (result.length !== 1 || ip !== result[0]) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Domain resolves to ${JSON.stringify(result)} instead of ${ip}`));
|
||||
|
||||
+2
-2
@@ -371,7 +371,7 @@ function checkDnsRecords(location, domain, callback) {
|
||||
getDnsRecords(location, domain, 'A', function (error, values) {
|
||||
if (error) return callback(error);
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
if (values.length === 0) return callback(null, { needsOverwrite: false }); // does not exist
|
||||
@@ -473,7 +473,7 @@ function prepareDashboardDomain(domain, auditSource, progressCallback, callback)
|
||||
|
||||
const adminFqdn = fqdn(constants.ADMIN_LOCATION, domainObject);
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
async.series([
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ function sync(auditSource, callback) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error);
|
||||
|
||||
let info = safe.JSON.parse(safe.fs.readFileSync(paths.DYNDNS_INFO_FILE, 'utf8')) || { ip: null };
|
||||
|
||||
+3
-3
@@ -275,7 +275,7 @@ function checkMx(domain, mailFqdn, callback) {
|
||||
dns.resolve(mxRecords[0].exchange, 'A', DNS_OPTIONS, function (error, mxIps) {
|
||||
if (error || mxIps.length !== 1) return callback(null, mx);
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(null, mx);
|
||||
|
||||
mx.status = mxIps[0] === ip;
|
||||
@@ -332,7 +332,7 @@ function checkPtr(mailFqdn, callback) {
|
||||
status: false
|
||||
};
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error, ptr);
|
||||
|
||||
ptr.domain = ip.split('.').reverse().join('.') + '.in-addr.arpa';
|
||||
@@ -415,7 +415,7 @@ const RBL_LIST = [
|
||||
function checkRblStatus(domain, callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
sysinfo.getPublicIp(function (error, ip) {
|
||||
sysinfo.getServerIp(function (error, ip) {
|
||||
if (error) return callback(error, ip);
|
||||
|
||||
var flippedIp = ip.split('.').reverse().join('.');
|
||||
|
||||
+1
-2
@@ -27,7 +27,6 @@ var appstore = require('./appstore.js'),
|
||||
semver = require('semver'),
|
||||
settings = require('./settings.js'),
|
||||
superagent = require('superagent'),
|
||||
sysinfo = require('./sysinfo.js'),
|
||||
users = require('./users.js'),
|
||||
tld = require('tldjs'),
|
||||
_ = require('underscore');
|
||||
@@ -264,7 +263,7 @@ function getStatus(callback) {
|
||||
callback(null, _.extend({
|
||||
version: constants.VERSION,
|
||||
apiServerOrigin: settings.apiServerOrigin(), // used by CaaS tool
|
||||
provider: sysinfo.provider(),
|
||||
provider: settings.provider(),
|
||||
cloudronName: cloudronName,
|
||||
adminFqdn: settings.adminDomain() ? settings.adminFqdn() : null,
|
||||
activated: activated,
|
||||
|
||||
@@ -15,13 +15,13 @@ var assert = require('assert'),
|
||||
HttpError = require('connect-lastmile').HttpError,
|
||||
HttpSuccess = require('connect-lastmile').HttpSuccess,
|
||||
provision = require('../provision.js'),
|
||||
sysinfo = require('../sysinfo.js'),
|
||||
settings = require('../settings.js'),
|
||||
superagent = require('superagent');
|
||||
|
||||
function providerTokenAuth(req, res, next) {
|
||||
assert.strictEqual(typeof req.body, 'object');
|
||||
|
||||
if (sysinfo.provider() === 'ami') {
|
||||
if (settings.provider() === 'ami') {
|
||||
if (typeof req.body.providerToken !== 'string' || !req.body.providerToken) return next(new HttpError(400, 'providerToken must be a non empty string'));
|
||||
|
||||
superagent.get('http://169.254.169.254/latest/meta-data/instance-id').timeout(30 * 1000).end(function (error, result) {
|
||||
|
||||
+39
-2
@@ -43,6 +43,11 @@ exports = module.exports = {
|
||||
getCloudronToken: getCloudronToken,
|
||||
setCloudronToken: setCloudronToken,
|
||||
|
||||
getSysinfoConfig: getSysinfoConfig,
|
||||
setSysinfoConfig: setSysinfoConfig,
|
||||
|
||||
provider: provider,
|
||||
|
||||
getAll: getAll,
|
||||
|
||||
initCache: initCache,
|
||||
@@ -69,6 +74,7 @@ exports = module.exports = {
|
||||
PLATFORM_CONFIG_KEY: 'platform_config',
|
||||
EXTERNAL_LDAP_KEY: 'external_ldap_config',
|
||||
REGISTRY_CONFIG_KEY: 'registry_config',
|
||||
SYSINFO_CONFIG_KEY: 'sysinfo_config',
|
||||
|
||||
// strings
|
||||
APP_AUTOUPDATE_PATTERN_KEY: 'app_autoupdate_pattern',
|
||||
@@ -133,6 +139,9 @@ let gDefaults = (function () {
|
||||
provider: 'noop'
|
||||
};
|
||||
result[exports.REGISTRY_CONFIG_KEY] = {};
|
||||
result[exports.SYSINFO_CONFIG_KEY] = {
|
||||
provider: 'generic'
|
||||
};
|
||||
result[exports.ADMIN_DOMAIN_KEY] = '';
|
||||
result[exports.ADMIN_FQDN_KEY] = '';
|
||||
result[exports.API_SERVER_ORIGIN_KEY] = 'https://api.cloudron.io';
|
||||
@@ -467,6 +476,30 @@ function setRegistryConfig(registryConfig, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function getSysinfoConfig(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
settingsdb.get(exports.SYSINFO_CONFIG_KEY, function (error, value) {
|
||||
if (error && error.reason === BoxError.NOT_FOUND) return callback(null, gDefaults[exports.SYSINFO_CONFIG_KEY]);
|
||||
if (error) return callback(error);
|
||||
|
||||
callback(null, JSON.parse(value));
|
||||
});
|
||||
}
|
||||
|
||||
function setSysinfoConfig(sysinfoConfig, callback) {
|
||||
assert.strictEqual(typeof sysinfoConfig, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
settingsdb.set(exports.SYSINFO_CONFIG_KEY, JSON.stringify(sysinfoConfig), function (error) {
|
||||
if (error) return callback(error);
|
||||
|
||||
notifyChange(exports.REGISTRY_CONFIG_KEY, sysinfoConfig);
|
||||
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
function getLicenseKey(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
@@ -554,7 +587,7 @@ function getAll(callback) {
|
||||
result[exports.DEMO_KEY] = !!result[exports.DEMO_KEY];
|
||||
|
||||
// convert JSON objects
|
||||
[exports.BACKUP_CONFIG_KEY, exports.PLATFORM_CONFIG_KEY, exports.EXTERNAL_LDAP_KEY, exports.REGISTRY_CONFIG_KEY ].forEach(function (key) {
|
||||
[exports.BACKUP_CONFIG_KEY, exports.PLATFORM_CONFIG_KEY, exports.EXTERNAL_LDAP_KEY, exports.REGISTRY_CONFIG_KEY, exports.SYSINFO_CONFIG_KEY ].forEach(function (key) {
|
||||
result[key] = typeof result[key] === 'object' ? result[key] : safe.JSON.parse(result[key]);
|
||||
});
|
||||
|
||||
@@ -568,12 +601,15 @@ function initCache(callback) {
|
||||
getAll(function (error, allSettings) {
|
||||
if (error) return callback(error);
|
||||
|
||||
const provider = safe.fs.readFileSync(paths.PROVIDER_FILE, 'utf8');
|
||||
|
||||
gCache = {
|
||||
apiServerOrigin: allSettings[exports.API_SERVER_ORIGIN_KEY],
|
||||
webServerOrigin: allSettings[exports.WEB_SERVER_ORIGIN_KEY],
|
||||
adminDomain: allSettings[exports.ADMIN_DOMAIN_KEY],
|
||||
adminFqdn: allSettings[exports.ADMIN_FQDN_KEY],
|
||||
isDemo: allSettings[exports.DEMO_KEY]
|
||||
isDemo: allSettings[exports.DEMO_KEY],
|
||||
provider: provider ? provider.trim() : 'generic'
|
||||
};
|
||||
|
||||
callback();
|
||||
@@ -614,6 +650,7 @@ function setApiServerOrigin(origin, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function provider() { return gCache.provider; }
|
||||
function apiServerOrigin() { return gCache.apiServerOrigin; }
|
||||
function webServerOrigin() { return gCache.webServerOrigin; }
|
||||
function adminDomain() { return gCache.adminDomain; }
|
||||
|
||||
+22
-6
@@ -12,12 +12,27 @@ let assert = require('assert'),
|
||||
once = require('once'),
|
||||
path = require('path'),
|
||||
paths = require('./paths.js'),
|
||||
sysinfo = require('./sysinfo.js');
|
||||
settings = require('./settings.js');
|
||||
|
||||
// the logic here is also used in the cloudron-support tool
|
||||
const AUTHORIZED_KEYS_FILEPATH = constants.TEST ? path.join(paths.baseDir(), 'authorized_keys') : ((sysinfo.provider() === 'ec2' || sysinfo.provider() === 'lightsail' || sysinfo.provider() === 'ami') ? '/home/ubuntu/.ssh/authorized_keys' : '/root/.ssh/authorized_keys'),
|
||||
AUTHORIZED_KEYS_USER = constants.TEST ? process.getuid() : ((sysinfo.provider() === 'ec2' || sysinfo.provider() === 'lightsail' || sysinfo.provider() === 'ami') ? 'ubuntu' : 'root'),
|
||||
AUTHORIZED_KEYS_CMD = path.join(__dirname, 'scripts/remotesupport.sh');
|
||||
const AUTHORIZED_KEYS_CMD = path.join(__dirname, 'scripts/remotesupport.sh');
|
||||
|
||||
function sshInfo() {
|
||||
let filePath, user;
|
||||
|
||||
if (constants.TEST) {
|
||||
filePath = path.join(paths.baseDir(), 'authorized_keys');
|
||||
user = process.getuid();
|
||||
} else if (settings.provider() === 'ec2' || settings.provider() === 'lightsail' || settings.provider() === 'ami') {
|
||||
filePath = '/home/ubuntu/.ssh/authorized_keys';
|
||||
user = 'ubuntu';
|
||||
} else {
|
||||
filePath = '/root/.ssh/authorized_keys';
|
||||
user = 'root';
|
||||
}
|
||||
|
||||
return { filePath, user };
|
||||
}
|
||||
|
||||
function getRemoteSupport(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
@@ -25,7 +40,7 @@ function getRemoteSupport(callback) {
|
||||
callback = once(callback); // exit may or may not be called after an 'error'
|
||||
|
||||
let result = '';
|
||||
let cp = shell.sudo('support', [ AUTHORIZED_KEYS_CMD, 'is-enabled', AUTHORIZED_KEYS_FILEPATH ], {}, function (error) {
|
||||
let cp = shell.sudo('support', [ AUTHORIZED_KEYS_CMD, 'is-enabled', sshInfo().filePath ], {}, function (error) {
|
||||
if (error) callback(new BoxError(BoxError.FS_ERROR, error));
|
||||
|
||||
callback(null, { enabled: result.trim() === 'true' });
|
||||
@@ -36,7 +51,8 @@ function getRemoteSupport(callback) {
|
||||
function enableRemoteSupport(enable, callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
shell.sudo('support', [ AUTHORIZED_KEYS_CMD, enable ? 'enable' : 'disable', AUTHORIZED_KEYS_FILEPATH, AUTHORIZED_KEYS_USER ], {}, function (error) {
|
||||
let si = sshInfo();
|
||||
shell.sudo('support', [ AUTHORIZED_KEYS_CMD, enable ? 'enable' : 'disable', si.filePath, si.user ], {}, function (error) {
|
||||
if (error) callback(new BoxError(BoxError.FS_ERROR, error));
|
||||
|
||||
callback();
|
||||
|
||||
+15
-21
@@ -1,35 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getPublicIp: getPublicIp,
|
||||
getServerIp: getServerIp,
|
||||
|
||||
hasIPv6: hasIPv6,
|
||||
provider: provider
|
||||
hasIPv6: hasIPv6
|
||||
};
|
||||
|
||||
var assert = require('assert'),
|
||||
ec2 = require('./sysinfo/ec2.js'),
|
||||
fs = require('fs'),
|
||||
generic = require('./sysinfo/generic.js'),
|
||||
paths = require('./paths.js'),
|
||||
scaleway = require('./sysinfo/scaleway.js'),
|
||||
safe = require('safetydance');
|
||||
settings = require('./settings.js');
|
||||
|
||||
let gProvider = null;
|
||||
|
||||
function provider() {
|
||||
if (gProvider) return gProvider;
|
||||
|
||||
gProvider = safe.fs.readFileSync(paths.PROVIDER_FILE, 'utf8');
|
||||
if (!gProvider) return gProvider = 'generic';
|
||||
|
||||
return gProvider.trim();
|
||||
}
|
||||
|
||||
function getApi(callback) {
|
||||
function getApi(config, callback) {
|
||||
assert.strictEqual(typeof config, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
switch (provider()) {
|
||||
const provider = config.provider || 'generic';
|
||||
|
||||
switch (provider) {
|
||||
case 'ec2': return callback(null, ec2);
|
||||
case 'lightsail': return callback(null, ec2);
|
||||
case 'ami': return callback(null, ec2);
|
||||
@@ -38,13 +28,17 @@ function getApi(callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function getPublicIp(callback) {
|
||||
function getServerIp(callback) {
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
getApi(function (error, api) {
|
||||
settings.getSysinfoConfig(function (error, config) {
|
||||
if (error) return callback(error);
|
||||
|
||||
api.getPublicIp(callback);
|
||||
getApi(config, function (error, api) {
|
||||
if (error) return callback(error);
|
||||
|
||||
api.getServerIp(config, callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getPublicIp: getPublicIp
|
||||
getServerIp: getServerIp
|
||||
};
|
||||
|
||||
var assert = require('assert'),
|
||||
@@ -9,7 +9,8 @@ var assert = require('assert'),
|
||||
superagent = require('superagent'),
|
||||
util = require('util');
|
||||
|
||||
function getPublicIp(callback) {
|
||||
function getServerIp(config, callback) {
|
||||
assert.strictEqual(typeof config, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
superagent.get('http://169.254.169.254/latest/meta-data/public-ipv4').timeout(30 * 1000).end(function (error, result) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getPublicIp: getPublicIp
|
||||
getServerIp: getServerIp
|
||||
};
|
||||
|
||||
var assert = require('assert'),
|
||||
@@ -9,7 +9,8 @@ var assert = require('assert'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
superagent = require('superagent');
|
||||
|
||||
function getPublicIp(callback) {
|
||||
function getServerIp(config, callback) {
|
||||
assert.strictEqual(typeof config, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
if (process.env.BOX_ENV === 'test') return callback(null, '127.0.0.1');
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
// -------------------------------------------
|
||||
|
||||
exports = module.exports = {
|
||||
getPublicIp: getPublicIp
|
||||
getServerIp: getServerIp
|
||||
};
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
function getPublicIp(callback) {
|
||||
function getServerIp(config, callback) {
|
||||
assert.strictEqual(typeo config, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
callback(new Error('not implemented'));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
getPublicIp: getPublicIp
|
||||
getServerIp: getServerIp
|
||||
};
|
||||
|
||||
var assert = require('assert'),
|
||||
@@ -9,7 +9,8 @@ var assert = require('assert'),
|
||||
superagent = require('superagent'),
|
||||
util = require('util');
|
||||
|
||||
function getPublicIp(callback) {
|
||||
function getServerIp(config, callback) {
|
||||
assert.strictEqual(typeof config, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
superagent.get('http://169.254.42.42/conf').timeout(30 * 1000).end(function (error, result) {
|
||||
|
||||
@@ -31,6 +31,7 @@ function setup(done) {
|
||||
database._clear,
|
||||
settings._setApiServerOrigin.bind(null, MOCK_API_SERVER_ORIGIN),
|
||||
settings.setAdmin.bind(null, ADMIN_DOMAIN, 'my.' + ADMIN_DOMAIN),
|
||||
settings.initCache
|
||||
], done);
|
||||
}
|
||||
|
||||
@@ -81,6 +82,7 @@ describe('Appstore', function () {
|
||||
expect(body.backendSettings.mailConfig.relayProviders).to.be.an('array');
|
||||
expect(body.backendSettings.appAutoupdatePattern).to.be.a('string');
|
||||
expect(body.backendSettings.boxAutoupdatePattern).to.be.a('string');
|
||||
expect(body.backendSettings.sysinfoProvider).to.be.a('string');
|
||||
expect(body.backendSettings.timeZone).to.be.a('string');
|
||||
expect(body.machine).to.be.an('object');
|
||||
expect(body.machine.cpus).to.be.an('array');
|
||||
|
||||
Reference in New Issue
Block a user