diff --git a/src/cloudron.js b/src/cloudron.js index 3220d64bf..2945a1a28 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -19,6 +19,8 @@ exports = module.exports = { renewCerts, syncDnsRecords, + updateDiskUsage, + runSystemChecks }; @@ -344,3 +346,9 @@ async function syncDnsRecords(options) { tasks.startTask(taskId, {}); return taskId; } + +async function updateDiskUsage() { + const taskId = await tasks.add(tasks.TASK_UPDATE_DISK_USAGE, []); + tasks.startTask(taskId, {}); + return taskId; +} diff --git a/src/paths.js b/src/paths.js index af72f9f34..29bfda241 100644 --- a/src/paths.js +++ b/src/paths.js @@ -38,6 +38,7 @@ exports = module.exports = { BACKUP_INFO_DIR: path.join(baseDir(), 'platformdata/backup'), UPDATE_DIR: path.join(baseDir(), 'platformdata/update'), UPDATE_CHECKER_FILE: path.join(baseDir(), 'platformdata/update/updatechecker.json'), + DISK_USAGE_FILE: path.join(baseDir(), 'platformdata/diskusage.json'), SNAPSHOT_INFO_FILE: path.join(baseDir(), 'platformdata/backup/snapshot-info.json'), DYNDNS_INFO_FILE: path.join(baseDir(), 'platformdata/dyndns-info.json'), DHPARAMS_FILE: path.join(baseDir(), 'platformdata/dhparams.pem'), diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 8af6e1b78..a9c3a1a90 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -10,6 +10,8 @@ exports = module.exports = { isRebootRequired, getConfig, getDisks, + getDiskUsage, + updateDiskUsage, getMemory, getUpdateInfo, update, @@ -169,6 +171,20 @@ async function getDisks(req, res, next) { next(new HttpSuccess(200, { disks: result })); } +async function getDiskUsage(req, res, next) { + const [error, result] = await safe(system.getDiskUsage()); + if (error) return next(BoxError.toHttpError(error)); + + next(new HttpSuccess(201, { usage: result })); +} + +async function updateDiskUsage(req, res, next) { + const [error, taskId] = await safe(cloudron.updateDiskUsage()); + if (error) return next(BoxError.toHttpError(error)); + + next(new HttpSuccess(201, { taskId })); +} + async function getMemory(req, res, next) { const [error, result] = await safe(system.getMemory()); if (error) return next(BoxError.toHttpError(error)); diff --git a/src/server.js b/src/server.js index 0c413a070..0e3fb7f2e 100644 --- a/src/server.js +++ b/src/server.js @@ -118,6 +118,8 @@ function initializeExpressSync() { router.post('/api/v1/cloudron/reboot', json, token, authorizeAdmin, routes.cloudron.reboot); router.get ('/api/v1/cloudron/graphs', token, authorizeAdmin, routes.graphs.getSystemGraphs); router.get ('/api/v1/cloudron/disks', token, authorizeAdmin, routes.cloudron.getDisks); + router.get ('/api/v1/cloudron/disk_usage', token, authorizeAdmin, routes.cloudron.getDiskUsage); + router.post('/api/v1/cloudron/disk_usage', token, authorizeAdmin, routes.cloudron.updateDiskUsage); router.get ('/api/v1/cloudron/memory', token, authorizeAdmin, routes.cloudron.getMemory); router.get ('/api/v1/cloudron/logs/:unit', token, authorizeAdmin, routes.cloudron.getLogs); router.get ('/api/v1/cloudron/logstream/:unit', token, authorizeAdmin, routes.cloudron.getLogStream); diff --git a/src/system.js b/src/system.js index 13a153b38..956f75037 100644 --- a/src/system.js +++ b/src/system.js @@ -5,7 +5,8 @@ exports = module.exports = { checkDiskSpace, getMemory, getMemoryAllocation, - du + getDiskUsage, + updateDiskUsage }; const apps = require('./apps.js'), @@ -145,3 +146,39 @@ function getMemoryAllocation(limit) { return Math.round(Math.round(limit * ratio) / 1048576) * 1048576; // nearest MB } + +function getDiskUsage() { + const output = safe.JSON.parse(safe.fs.readFileSync(paths.DISK_USAGE_FILE, 'utf8')); + return output; +} + +async function updateDiskUsage(progressCallback) { + assert.strictEqual(progressCallback, 'function'); + + const disks = await getDisks(); + const filesystems = Object.keys(disks); + const now = Date.now(); + + let percent = 1; + + for (const filesystem of filesystems) { + const disk = disks[filesystem]; + + percent += (100/filesystems.length); + progressCallback({ percent, message: `Checking contents of ${filesystem}`}); + + for (const content of disk.contents) { + progressCallback({ message: `Checking du of ${JSON.stringify(content)}`}); + if (content.type === 'docker') { + content.usage = (await docker.du()).LayersSize; + } else { + content.usage = await du(content.path, { exclude: content.exclude }); + } + progressCallback({ message: `du of ${JSON.stringify(content)}: ${content.usage}`}); + } + } + + if (!safe.fs.writeFileSync(paths.DISK_USAGE_FILE, JSON.stringify({ ts: now, disks }), 'utf8')) throw new BoxError(BoxError.FS_ERROR, `Could not write du cache file: ${safe.error.message}`); + + return disks; +} diff --git a/src/tasks.js b/src/tasks.js index a2d11cce2..c0ee9d6a2 100644 --- a/src/tasks.js +++ b/src/tasks.js @@ -28,6 +28,7 @@ exports = module.exports = { TASK_SYNC_EXTERNAL_LDAP: 'syncExternalLdap', TASK_CHANGE_MAIL_LOCATION: 'changeMailLocation', TASK_SYNC_DNS_RECORDS: 'syncDnsRecords', + TASK_UPDATE_DISK_USAGE: 'updateDiskUsage', // error codes ESTOPPED: 'stopped', diff --git a/src/taskworker.js b/src/taskworker.js index 3a5715809..46c9ccf50 100755 --- a/src/taskworker.js +++ b/src/taskworker.js @@ -15,6 +15,7 @@ const apptask = require('./apptask.js'), reverseProxy = require('./reverseproxy.js'), safe = require('safetydance'), settings = require('./settings.js'), + system = require('./system.js'), tasks = require('./tasks.js'), updater = require('./updater.js'); @@ -28,6 +29,7 @@ const TASKS = { // indexed by task type syncExternalLdap: externalLdap.sync, changeMailLocation: mail.changeLocation, syncDnsRecords: dns.syncDnsRecords, + updateDiskUsage: system.updateDiskUsage, _identity: async (arg, progressCallback) => { progressCallback(); return arg; }, _error: async (arg, progressCallback) => { progressCallback(); throw new Error(`Failed for arg: ${arg}`); },