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' ) ,
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-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-08-20 09:19:44 -07:00
const allApps = await apps . list ( ) ;
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-08-20 09:19:44 -07:00
return appDisks ;
2021-01-04 11:05:42 -08:00
}
2021-08-25 19:41:46 -07:00
async function getBackupsFilesystem ( ) {
2021-08-19 13:24:38 -07:00
const backupConfig = await settings . getBackupConfig ( ) ;
2021-01-04 11:05:42 -08:00
2021-08-19 13:24:38 -07:00
if ( backupConfig . provider !== 'filesystem' ) return null ;
2021-01-04 11:05:42 -08:00
2021-08-19 13:24:38 -07:00
const result = await df . file ( backupConfig . backupFolder ) ;
return result . filesystem ;
2021-01-04 11:05:42 -08:00
}
2021-08-25 19:41:46 -07:00
async function getDisks ( ) {
const info = await docker . info ( ) ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
let [ error , allDisks ] = await safe ( df ( ) ) ;
if ( error ) throw new BoxError ( BoxError . FS _ERROR , error ) ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
// filter by ext4 and then sort to make sure root disk is first
const ext4Disks = allDisks . filter ( ( r ) => r . type === 'ext4' ) . sort ( ( a , b ) => a . mountpoint . localeCompare ( b . mountpoint ) ) ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
const diskInfos = [ ] ;
for ( const p of [ paths . BOX _DATA _DIR , paths . PLATFORM _DATA _DIR , paths . APPS _DATA _DIR , info . DockerRootDir ] ) {
const [ dfError , diskInfo ] = await safe ( df . file ( p ) ) ;
if ( dfError ) throw new BoxError ( BoxError . FS _ERROR , dfError ) ;
diskInfos . push ( diskInfo ) ;
}
const backupsFilesystem = await getBackupsFilesystem ( ) ;
const result = {
disks : ext4Disks , // root disk is first. { filesystem, type, size, used, avialable, capacity, mountpoint }
boxDataDisk : diskInfos [ 0 ] . filesystem ,
mailDataDisk : diskInfos [ 0 ] . filesystem ,
platformDataDisk : diskInfos [ 1 ] . filesystem ,
appsDataDisk : diskInfos [ 2 ] . filesystem ,
dockerDataDisk : diskInfos [ 3 ] . filesystem ,
backupsDisk : backupsFilesystem ,
apps : { } , // filled below
volumes : { } // filled below
} ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
result . apps = await getAppDisks ( result . appsDataDisk ) ;
result . volumes = await getVolumeDisks ( result . appsDataDisk ) ;
2021-06-23 23:38:39 -07:00
2021-08-25 19:41:46 -07:00
return result ;
}
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
async function checkDiskSpace ( ) {
debug ( 'checkDiskSpace: checking disk space' ) ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
const disks = await getDisks ( ) ;
2021-06-23 23:38:39 -07:00
2021-08-25 19:41:46 -07:00
let markdownMessage = '' ;
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
disks . disks . forEach ( function ( entry ) {
// ignore other filesystems but where box, app and platform data is
if ( entry . filesystem !== disks . boxDataDisk
&& entry . filesystem !== disks . platformDataDisk
&& entry . filesystem !== disks . appsDataDisk
&& entry . filesystem !== disks . backupsDisk
&& entry . filesystem !== disks . dockerDataDisk ) return false ;
2021-06-03 12:20:44 -07:00
2021-08-25 19:41:46 -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-08-25 19:41:46 -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 } ` ;
await notifications . alert ( notifications . ALERT _DISK _SPACE , 'Server is running out of disk space' , markdownMessage ) ;
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 ;
}
2021-08-25 19:41:46 -07:00
async function getMemory ( ) {
return {
2019-11-21 12:55:17 -08:00
memory : os . totalmem ( ) ,
2021-01-20 11:45:04 -08:00
swap : getSwapSize ( )
2021-08-25 19:41:46 -07:00
} ;
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
}