-
-
Notifications
You must be signed in to change notification settings - Fork 229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(doctor): add free disk space check #1284
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const sysinfo = require('systeminformation'); | ||
const {SystemError} = require('../../../errors'); | ||
|
||
const MB_IN_BYTES = 1048576; | ||
|
||
// one version of Ghost takes ~400mb of space currently, to be safe we're going to say 1gb | ||
const MIN_FREE_SPACE = 1024; | ||
|
||
async function checkFreeSpace(ctx) { | ||
const dir = ctx.instance ? ctx.instance.dir : process.cwd(); | ||
|
||
const disks = await sysinfo.fsSize(); | ||
|
||
// filter out disks with matching mount points, then sort in descending order by mount point length | ||
// to get the mount point with greatest specificity | ||
const [disk] = disks.filter(d => dir.startsWith(d.mount)).sort((a, b) => b.mount.length - a.mount.length); | ||
|
||
if (!disk) { | ||
// couldn't find a matching disk, early return | ||
// TODO: maybe throw a warning of some sort here? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could have a warning that says There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. eventually I want to have the doctor command actually distinguish between "warnings" and "errors" - left this comment to remember to add a warning here whenever that distinction is possible. |
||
return; | ||
} | ||
|
||
const available = (disk.size - disk.used) / MB_IN_BYTES; | ||
if (available < MIN_FREE_SPACE) { | ||
throw new SystemError(`You are recommended to have at least ${MIN_FREE_SPACE} MB of free storage space available for smooth operation. It looks like you have ~${available} MB available`); | ||
} | ||
} | ||
|
||
module.exports = { | ||
title: 'Checking free space', | ||
task: checkFreeSpace, | ||
category: ['install', 'update'] | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
const {expect} = require('chai'); | ||
const sinon = require('sinon'); | ||
|
||
const sysinfo = require('systeminformation'); | ||
const {SystemError} = require('../../../../../lib/errors'); | ||
|
||
const check = require('../../../../../lib/commands/doctor/checks/free-space'); | ||
|
||
describe('Unit: Doctor Checks > Free Space', function () { | ||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
it('exports proper task', function () { | ||
expect(check.title).to.equal('Checking free space'); | ||
expect(check.task).to.be.a('function'); | ||
expect(check.category).to.deep.equal(['install', 'update']); | ||
}); | ||
|
||
it('handles error from systeminformation', function () { | ||
const stub = sinon.stub(sysinfo, 'fsSize').rejects(new Error('test-error')); | ||
const cwdStub = sinon.stub(process, 'cwd').returns('/test/directory'); | ||
|
||
return check.task({}).catch((error) => { | ||
expect(error).to.be.an('error'); | ||
expect(error.message).to.equal('test-error'); | ||
expect(stub.calledOnce).to.be.true; | ||
expect(cwdStub.calledOnce).to.be.true; | ||
}); | ||
}); | ||
|
||
it('handles error from systeminformation', function () { | ||
const stub = sinon.stub(sysinfo, 'fsSize').rejects(new Error('test-error')); | ||
const cwdStub = sinon.stub(process, 'cwd').returns('/test/directory'); | ||
|
||
return check.task({}).catch((error) => { | ||
expect(error).to.be.an('error'); | ||
expect(error.message).to.equal('test-error'); | ||
expect(stub.calledOnce).to.be.true; | ||
expect(cwdStub.calledOnce).to.be.true; | ||
}); | ||
}); | ||
|
||
it('does nothing if no matching mount points found', async function () { | ||
const stub = sinon.stub(sysinfo, 'fsSize').resolves([{ | ||
mount: '/not/matching/dir', | ||
size: 0, | ||
used: 0 | ||
}]); | ||
const cwdStub = sinon.stub(process, 'cwd').returns('/not/matching/dir'); | ||
|
||
await check.task({instance: {dir: '/test/dir'}}); | ||
expect(stub.calledOnce).to.be.true; | ||
expect(cwdStub.called).to.be.false; | ||
}); | ||
|
||
it('errors if not enough space available', function () { | ||
const stub = sinon.stub(sysinfo, 'fsSize').resolves([{ | ||
mount: '/', | ||
size: 1000000000000, | ||
used: 1000000000 | ||
}, { | ||
mount: '/test/dir', | ||
size: 0, | ||
used: 0 | ||
}]); | ||
const cwdStub = sinon.stub(process, 'cwd').returns('/test/dir'); | ||
|
||
return check.task({}).catch((error) => { | ||
expect(error).to.be.an.instanceOf(SystemError); | ||
expect(stub.calledOnce).to.be.true; | ||
expect(cwdStub.calledOnce).to.be.true; | ||
}); | ||
}); | ||
|
||
it('succeeds if enough space available', async function () { | ||
const stub = sinon.stub(sysinfo, 'fsSize').resolves([{ | ||
mount: '/', | ||
size: 1000000000000, | ||
used: 1000000000 | ||
}, { | ||
mount: '/test/dir', | ||
size: 0, | ||
used: 0 | ||
}]); | ||
const cwdStub = sinon.stub(process, 'cwd').returns('/test/dir'); | ||
|
||
await check.task({instance: {dir: '/'}}); | ||
expect(stub.calledOnce).to.be.true; | ||
expect(cwdStub.called).to.be.false; | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't wait to
ctx.instance?.dir ?? process.cwd()
in the futureThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯