Skip to content

Commit

Permalink
Merge pull request #309 from FlowFuse/308-disable-auto-safe-mode-option
Browse files Browse the repository at this point in the history
Add "disable auto safe mode" option
  • Loading branch information
hardillb authored Dec 16, 2024
2 parents 4bbf9cd + 36d4609 commit c3a1b52
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 11 deletions.
16 changes: 12 additions & 4 deletions lib/launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const LogBuffer = require('./logBuffer')
const { getSettingsFile } = require('./runtimeSettings')
const SampleBuffer = require('./resources/sampleBuffer')
const resourceSample = require('./resources/sample')
const hasProperty = (object, property) => !!(object && Object.prototype.hasOwnProperty.call(object, property))

/** The point at which we check to see if we are in a boot loop */
const MAX_RESTART_COUNT = 5
Expand Down Expand Up @@ -86,6 +87,8 @@ class Launcher {
this.sampleBuffer = new SampleBuffer(this.options.sampleBufferMax || 2520)
this.cpuAuditLogged = 0
this.memoryAuditLogged = 0

this.disableAutoSafeMode = false
}

/** @type {Number} */
Expand All @@ -101,7 +104,7 @@ class Launcher {
const settingsURL = `${this.options.forgeURL}/api/v1/projects/${this.options.project}/settings`
let newSettings
try {
newSettings = await got(settingsURL, {
newSettings = await got.get(settingsURL, {
headers: {
authorization: `Bearer ${this.options.token}`
}
Expand Down Expand Up @@ -149,7 +152,12 @@ class Launcher {
nodesDir.push(require.main.path)

this.settings.nodesDir = nodesDir

if (hasProperty(this.settings, 'disableAutoSafeMode') && typeof this.settings.disableAutoSafeMode === 'boolean') {
this.disableAutoSafeMode = this.settings.disableAutoSafeMode
}
if (this.disableAutoSafeMode === true) {
this.logBuffer.add({ level: 'system', msg: 'Auto Safe Mode is disabled.' })
}
const settingsFileContent = getSettingsFile(this.settings)
const settingsPath = path.join(this.settings.rootDir, this.settings.userDir, 'settings.js')
this.targetState = this.settings.state || States.RUNNING
Expand Down Expand Up @@ -464,8 +472,8 @@ class Launcher {

this.logBuffer.add({ level: 'system', msg: `Node-RED unexpectedly stopped after: ${Math.round(duration / 1000)}s` })

// if start count == MAX_RESTART_COUNT, then check for boot loop
if (this.startTimes.length === MAX_RESTART_COUNT) {
// if auto-safe-mode is not disabled && start count == MAX_RESTART_COUNT, then check for boot loop
if (this.disableAutoSafeMode !== true && this.startTimes.length === MAX_RESTART_COUNT) {
// calculate the average runtime
const avg = this.runDurations.reduce((a, b) => a + b, 0) / this.runDurations.length

Expand Down
74 changes: 67 additions & 7 deletions test/unit/lib/launcher_spec.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,97 @@
const should = require('should') // eslint-disable-line
const sinon = require('sinon')
const got = require('got')
const fs = require('fs')
const launcher = require('../../../lib/launcher.js')

describe('Launcher', function () {
it('should create a new launcher', async function () {
const l = new launcher.Launcher({})
should.exist(l)
})
this.afterEach(function () {
sinon.restore()
})
describe('health check', function () {
it('has a default value', async function () {
const l = new launcher.Launcher({})
l.should.have.property('healthCheckInterval', 7499)
})
it('can be set by user', async function () {
const l = new launcher.Launcher({})
sinon.stub(l, 'loadSettings').callsFake(() => {
l.settings = {
healthCheckInterval: 1234
l.should.have.property('healthCheckInterval', 7499) // initial/default value is 7499
sinon.stub(fs, 'writeFileSync').callsFake(() => {})
sinon.stub(l, 'updatePackage').callsFake(() => {})
sinon.stub(got, 'get').callsFake(() => {
return {
json () {
return {
forgeURL: 'http://localhost:1880',
rootDir: '/path/to/node-red',
userDir: '/path/to/.node-red',
settings: {
palette: {}
},
healthCheckInterval: 1234
}
}
}
})
await l.loadSettings()
l.should.have.property('healthCheckInterval', 1234)
l.should.have.property('healthCheckInterval', 1234) // when loaded from API, it should now be updated
})
it('cannot be less than 1 second', async function () {
const l = new launcher.Launcher({})
sinon.stub(l, 'loadSettings').callsFake(() => {
l.settings = {
healthCheckInterval: 999
sinon.stub(fs, 'writeFileSync').callsFake(() => {})
sinon.stub(l, 'updatePackage').callsFake(() => {})
sinon.stub(got, 'get').callsFake(() => {
return {
json () {
return {
forgeURL: 'http://localhost:1880',
rootDir: '/path/to/node-red',
userDir: '/path/to/.node-red',
settings: {
palette: {}
},
healthCheckInterval: 999
}
}
}
})
await l.loadSettings()
// returns the default value when the user sets the value out of range
l.should.have.property('healthCheckInterval', 7499)
})
})

describe('disable auto safe mode', function () {
it('is disabled by default', async function () {
const l = new launcher.Launcher({})
l.should.have.property('disableAutoSafeMode', false)
})
it('can be set by user', async function () {
const l = new launcher.Launcher({})
sinon.stub(fs, 'writeFileSync').callsFake(() => {})
sinon.stub(l, 'updatePackage').callsFake(() => {})
sinon.stub(got, 'get').callsFake(() => {
return {
json () {
return {
forgeURL: 'http://localhost:1880',
rootDir: '/path/to/node-red',
userDir: '/path/to/.node-red',
settings: {
palette: {}
},
disableAutoSafeMode: true
}
}
}
})

await l.loadSettings()
l.should.have.property('disableAutoSafeMode', true)
})
})
})

0 comments on commit c3a1b52

Please sign in to comment.