2019-08-19 13:50:44 -07:00
'use strict' ;
exports = module . exports = {
2021-01-04 11:05:42 -08:00
getDisks ,
checkDiskSpace ,
2021-01-20 11:45:04 -08:00
getMemory ,
getMemoryAllocation
2019-08-19 13:50:44 -07:00
} ;
const apps = require ( './apps.js' ) ,
assert = require ( 'assert' ) ,
async = require ( 'async' ) ,
2019-10-22 11:11:41 -07:00
BoxError = require ( './boxerror.js' ) ,
2019-08-19 13:50:44 -07:00
debug = require ( 'debug' ) ( 'box:disks' ) ,
df = require ( '@sindresorhus/df' ) ,
docker = require ( './docker.js' ) ,
notifications = require ( './notifications.js' ) ,
2019-11-21 12:55:17 -08:00
os = require ( 'os' ) ,
paths = require ( './paths.js' ) ,
2020-01-31 13:37:07 -08:00
safe = require ( 'safetydance' ) ,
2021-01-04 11:05:42 -08:00
settings = require ( './settings.js' ) ,
volumes = require ( './volumes.js' ) ;
2019-08-19 13:50:44 -07:00
2021-01-04 11:05:42 -08:00
const dfAsync = async . asyncify ( df ) , dfFileAsync = async . asyncify ( df . file ) ;
2021-05-11 17:50:48 -07:00
async function getVolumeDisks ( appsDataDisk ) {
2021-01-27 21:47:36 -08:00
assert . strictEqual ( typeof appsDataDisk , 'string' ) ;
2019-08-19 13:50:44 -07:00
2021-01-04 11:05:42 -08:00
let volumeDisks = { } ;
2021-05-11 17:50:48 -07:00
const allVolumes = await volumes . list ( ) ;
2021-01-04 11:05:42 -08:00
2021-05-11 17:50:48 -07:00
for ( const volume of allVolumes ) {
const [ error , result ] = await safe ( df ( volume . hostPath ) ) ;
volumeDisks [ volume . id ] = error ? appsDataDisk : result . filesystem ; // ignore any errors
}
2021-01-04 11:05:42 -08:00
2021-05-11 17:50:48 -07:00
return volumeDisks ;
2021-01-04 11:05:42 -08:00
}
2021-05-11 17:50:48 -07:00
async function getAppDisks ( appsDataDisk ) {
2021-01-27 21:47:36 -08:00
assert . strictEqual ( typeof appsDataDisk , 'string' ) ;
2021-01-04 11:05:42 -08:00
let appDisks = { } ;
2021-05-11 17:50:48 -07:00
return new Promise ( ( resolve , reject ) => {
apps . getAll ( async function ( error , allApps ) {
if ( error ) return reject ( error ) ;
for ( const app of allApps ) {
if ( ! app . dataDir ) {
appDisks [ app . id ] = appsDataDisk ;
} else {
const [ error , result ] = await safe ( df . file ( app . dataDir ) ) ;
appDisks [ app . id ] = error ? appsDataDisk : result . filesystem ; // ignore any errors
}
2021-01-04 11:05:42 -08:00
}
2021-05-11 17:50:48 -07:00
resolve ( appDisks ) ;
2021-01-04 11:05:42 -08:00
} ) ;
} ) ;
}
function getBackupDisk ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settings . getBackupConfig ( function ( error , backupConfig ) {
if ( error ) return callback ( error ) ;
if ( backupConfig . provider !== 'filesystem' ) return callback ( null , null ) ;
dfFileAsync ( backupConfig . backupFolder , function ( error , result ) {
if ( error ) return callback ( error ) ;
callback ( null , result . filesystem ) ;
} ) ;
} ) ;
}
function getDisks ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2019-08-19 13:50:44 -07:00
docker . info ( function ( error , info ) {
2019-10-22 11:11:41 -07:00
if ( error ) return callback ( error ) ;
2019-08-19 13:50:44 -07:00
async . series ( [
dfAsync ,
dfFileAsync . bind ( null , paths . BOX _DATA _DIR ) ,
dfFileAsync . bind ( null , paths . PLATFORM _DATA _DIR ) ,
dfFileAsync . bind ( null , paths . APPS _DATA _DIR ) ,
2021-01-04 11:05:42 -08:00
dfFileAsync . bind ( null , info . DockerRootDir ) ,
getBackupDisk ,
2021-05-11 17:50:48 -07:00
] , async function ( error , values ) {
2019-10-22 11:11:41 -07:00
if ( error ) return callback ( new BoxError ( BoxError . FS _ERROR , error ) ) ;
2019-08-19 13:50:44 -07:00
// filter by ext4 and then sort to make sure root disk is first
const ext4Disks = values [ 0 ] . filter ( ( r ) => r . type === 'ext4' ) . sort ( ( a , b ) => a . mountpoint . localeCompare ( b . mountpoint ) ) ;
const disks = {
2020-04-01 16:26:16 -07:00
disks : ext4Disks , // root disk is first. { filesystem, type, size, used, avialable, capacity, mountpoint }
2019-08-19 13:50:44 -07:00
boxDataDisk : values [ 1 ] . filesystem ,
mailDataDisk : values [ 1 ] . filesystem ,
platformDataDisk : values [ 2 ] . filesystem ,
appsDataDisk : values [ 3 ] . filesystem ,
dockerDataDisk : values [ 4 ] . filesystem ,
2021-01-04 11:05:42 -08:00
backupsDisk : values [ 5 ] ,
2021-01-27 21:47:36 -08:00
apps : { } , // filled below
volumes : { } // filled below
2019-08-19 13:50:44 -07:00
} ;
2021-05-11 17:50:48 -07:00
[ error , disks . apps ] = await safe ( getAppDisks ( disks . appsDataDisk ) ) ;
if ( error ) return callback ( error ) ;
2021-01-27 21:47:36 -08:00
2021-05-11 17:50:48 -07:00
[ error , disks . volumes ] = await safe ( getVolumeDisks ( disks . appsDataDisk ) ) ;
if ( error ) return callback ( error ) ;
2021-01-27 21:47:36 -08:00
2021-05-11 17:50:48 -07:00
callback ( null , disks ) ;
2019-08-19 13:50:44 -07:00
} ) ;
} ) ;
}
function checkDiskSpace ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2021-06-23 23:38:39 -07:00
debug ( 'checkDiskSpace: checking disk space' ) ;
2019-08-19 13:50:44 -07:00
2021-06-03 12:20:44 -07:00
getDisks ( async function ( error , disks ) {
2019-08-19 13:50:44 -07:00
if ( error ) {
debug ( 'checkDiskSpace: error getting disks %s' , error . message ) ;
return callback ( ) ;
}
2021-06-23 23:38:39 -07:00
let markdownMessage = '' ;
disks . disks . forEach ( function ( entry ) {
2019-08-19 13:50:44 -07:00
// ignore other filesystems but where box, app and platform data is
if ( entry . filesystem !== disks . boxDataDisk
&& entry . filesystem !== disks . platformDataDisk
&& entry . filesystem !== disks . appsDataDisk
2020-01-31 22:20:34 -08:00
&& entry . filesystem !== disks . backupsDisk
2019-08-19 13:50:44 -07:00
&& entry . filesystem !== disks . dockerDataDisk ) return false ;
2021-06-23 23:38:39 -07:00
if ( entry . available <= ( 1.25 * 1024 * 1024 * 1024 ) ) { // 1.5G
markdownMessage += ` * ${ entry . filesystem } is at ${ entry . capacity * 100 } % capacity. \n ` ;
}
2019-08-19 13:50:44 -07:00
} ) ;
2021-06-23 23:38:39 -07:00
debug ( ` checkDiskSpace: disk space checked. out of space: ${ markdownMessage || 'no' } ` ) ;
if ( markdownMessage ) markdownMessage = ` One or more file systems are running out of space. Please increase the disk size at the earliest. \n \n ${ markdownMessage } ` ;
2019-08-19 13:50:44 -07:00
2021-06-23 23:38:39 -07:00
await notifications . alert ( notifications . ALERT _DISK _SPACE , 'Server is running out of disk space' , markdownMessage ) ;
2021-06-03 12:20:44 -07:00
callback ( ) ;
2019-08-19 13:50:44 -07:00
} ) ;
}
2019-11-21 12:55:17 -08:00
2021-01-20 11:45:04 -08:00
function getSwapSize ( ) {
2019-11-21 12:55:17 -08:00
const stdout = safe . child _process . execSync ( 'swapon --noheadings --raw --bytes --show=SIZE' , { encoding : 'utf8' } ) ;
2019-11-21 13:48:17 -08:00
const swap = ! stdout ? 0 : stdout . trim ( ) . split ( '\n' ) . map ( x => parseInt ( x , 10 ) || 0 ) . reduce ( ( acc , cur ) => acc + cur ) ;
2019-11-21 12:55:17 -08:00
2021-01-20 11:45:04 -08:00
return swap ;
}
function getMemory ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2019-11-21 12:55:17 -08:00
callback ( null , {
memory : os . totalmem ( ) ,
2021-01-20 11:45:04 -08:00
swap : getSwapSize ( )
2019-11-21 12:55:17 -08:00
} ) ;
}
2021-01-20 11:45:04 -08:00
function getMemoryAllocation ( limit ) {
2021-01-20 20:05:39 -08:00
let ratio = parseFloat ( safe . fs . readFileSync ( paths . SWAP _RATIO _FILE , 'utf8' ) , 10 ) ;
if ( ! ratio ) {
const pc = os . totalmem ( ) / ( os . totalmem ( ) + getSwapSize ( ) ) ;
ratio = Math . round ( pc * 10 ) / 10 ; // a simple ratio
}
2021-01-20 11:45:04 -08:00
return Math . round ( Math . round ( limit * ratio ) / 1048576 ) * 1048576 ; // nearest MB
}