Skip to content
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

Add "disable auto safe mode" option #309

Merged
merged 7 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
})
})
})
Loading