Skip to content

Commit

Permalink
add events to agent
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Dec 17, 2024
1 parent 3765240 commit 924e36d
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 17 deletions.
45 changes: 28 additions & 17 deletions lib/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const {
const synthetics = require('./synthetics')
const Harvester = require('./harvester')
const { createFeatureUsageMetrics } = require('./util/application-logging')
const HealthReporter = require('./health-reporter')

// Map of valid states to whether or not data collection is valid
const STATES = {
Expand Down Expand Up @@ -159,7 +160,10 @@ const DEFAULT_HARVEST_INTERVAL_MS = 60000
function Agent(config) {
EventEmitter.call(this)

this.healthReporter = new HealthReporter()

if (!config) {
this.healthReporter.setStatus(HealthReporter.STATUS_CONFIG_PARSE_FAILURE)
throw new Error('Agent must be created with a configuration!')
}

Expand Down Expand Up @@ -350,11 +354,15 @@ Agent.prototype.start = function start(callback) {
'Has a license key been specified in the agent configuration ' +
'file or via the NEW_RELIC_LICENSE_KEY environment variable?'
)
this.healthReporter.setStatus(HealthReporter.STATUS_LICENSE_KEY_MISSING)

this.setState('errored')
sampler.stop()
const self = this
return process.nextTick(function onNextTick() {
callback(new Error('Not starting without license key!'))
self.healthReporter.stop(() => {
callback(new Error('Not starting without license key!'))
})
})
}
logger.info('Starting New Relic for Node.js connection process.')
Expand Down Expand Up @@ -484,23 +492,26 @@ Agent.prototype.stop = function stop(callback) {

sampler.stop()

if (this.collector.isConnected()) {
this.collector.shutdown(function onShutdown(error) {
if (error) {
agent.setState('errored')
logger.warn(error, 'Got error shutting down connection to New Relic:')
} else {
agent.setState('stopped')
logger.info('Stopped New Relic for Node.js.')
}

callback(error)
})
} else {
logger.trace('Collector was not connected, invoking callback.')
this.healthReporter.setStatus(HealthReporter.STATUS_AGENT_SHUTDOWN)
this.healthReporter.stop(() => {
if (agent.collector.isConnected()) {
agent.collector.shutdown(function onShutdown(error) {
if (error) {
agent.setState('errored')
logger.warn(error, 'Got error shutting down connection to New Relic:')
} else {
agent.setState('stopped')
logger.info('Stopped New Relic for Node.js.')
}

callback(error)
})
} else {
logger.trace('Collector was not connected, invoking callback.')

process.nextTick(callback)
}
process.nextTick(callback)
}
})
}

/**
Expand Down
10 changes: 10 additions & 0 deletions lib/health-reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function writeStatus({ file, healthy = true, code, msg, startTime, callback } =
* by the environment.
*/
class HealthReporter {
#enabled = false
#status = HealthReporter.STATUS_HEALTHY
#interval
#destFile
Expand Down Expand Up @@ -101,6 +102,7 @@ class HealthReporter {
this.#interval = setInterval(this.#healthCheck.bind(this), checkInterval)
this.#interval.unref()

this.#enabled = true
this.#logger.info('health reporter initialized')
}

Expand Down Expand Up @@ -128,6 +130,10 @@ class HealthReporter {
* @param {string} status Utilize one of the static status fields.
*/
setStatus(status) {
if (this.#enabled === false) {
return
}

if (VALID_CODES.has(status) === false) {
// TODO: if we ever add codes in our reserved block (300-399), account for them here
this.#logger.warn(`invalid health reporter status provided: ${status}`)
Expand Down Expand Up @@ -157,6 +163,10 @@ class HealthReporter {
* been updated.
*/
stop(done) {
if (this.#enabled === false) {
return done()
}

clearInterval(this.#interval)

const healthy = this.#status === HealthReporter.STATUS_HEALTHY
Expand Down
83 changes: 83 additions & 0 deletions test/unit/agent/agent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const test = require('node:test')
const assert = require('node:assert')
const tspl = require('@matteo.collina/tspl')
const Collector = require('../../lib/test-collector')

const sinon = require('sinon')
Expand All @@ -17,12 +18,51 @@ const Agent = require('../../../lib/agent')
const Transaction = require('../../../lib/transaction')
const CollectorResponse = require('../../../lib/collector/response')

const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
process.env.NEW_RELIC_SUPERAGENT_FLEET_ID = 42
process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION = os.tmpdir()
process.env.NEW_RELIC_SUPERAGENT_HEALTH_FREQUENCY = 1
const HealthReporter = require('../../../lib/health-reporter')
test.after(() => {
const files = fs.readdirSync(process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION)
for (const file of files) {
if (file.startsWith('health-') !== true) {
continue
}
fs.rmSync(path.join(process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION, file), {
force: true
})
}
delete process.env.NEW_RELIC_SUPERAGENT_FLEET_ID
delete process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION
delete process.env.NEW_RELIC_SUPERAGENT_HEALTH_FREQUENCY
})

const RUN_ID = 1337

test('should require configuration passed to constructor', () => {
assert.throws(() => new Agent())
})

test('should update health reporter if configuration is bad', (t, end) => {
const setStatus = HealthReporter.prototype.setStatus
t.after(() => {
HealthReporter.prototype.setStatus = setStatus
})

HealthReporter.prototype.setStatus = (status) => {
assert.equal(status, HealthReporter.STATUS_CONFIG_PARSE_FAILURE)
end()
}

try {
const _ = new Agent()
assert.ok(_, 'should not be hit, just satisfying linter')
} catch {}
})

test('should not throw with valid config', () => {
const config = configurator.initialize({ agent_enabled: false })
const agent = new Agent(config)
Expand Down Expand Up @@ -310,6 +350,29 @@ test('when starting', async (t) => {
})
})

await t.test('should error when no license key is included, and update health', async (t) => {
const plan = tspl(t, { plan: 2 })
const { agent } = t.nr
const setStatus = HealthReporter.prototype.setStatus
t.after(() => {
HealthReporter.prototype.setStatus = setStatus
})

HealthReporter.prototype.setStatus = (status) => {
plan.equal(status, HealthReporter.STATUS_LICENSE_KEY_MISSING)
}

agent.config.license_key = undefined
agent.collector.connect = function () {
plan.fail('should not be called')
}
agent.start((error) => {
plan.equal(error.message, 'Not starting without license key!')
})

await plan.completed
})

await t.test('should call connect when using proxy', (t, end) => {
const { agent } = t.nr
agent.config.proxy = 'fake://url'
Expand Down Expand Up @@ -462,6 +525,26 @@ test('when stopping', async (t) => {
assert.equal(sampler.state, 'stopped')
})

await t.test('should stop health reporter', async (t) => {
const plan = tspl(t, { plan: 1 })
const setStatus = HealthReporter.prototype.setStatus

t.after(() => {
HealthReporter.prototype.setStatus = setStatus
})

HealthReporter.prototype.setStatus = (status) => {
plan.equal(status, HealthReporter.STATUS_AGENT_SHUTDOWN)
}

const { agent } = t.nr
sampler.start(agent)
agent.collector.shutdown = () => {}
agent.stop(() => {})

await plan.completed
})

await t.test('should change state to "stopping"', (t) => {
const { agent } = t.nr
sampler.start(agent)
Expand Down
13 changes: 13 additions & 0 deletions test/unit/lib/health-reporter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ test('logs error if writing failed', async (t) => {
}
})

test('setStatus and stop do nothing if reporter disabled', (t, end) => {
delete process.env.NEW_RELIC_SUPERAGENT_FLEET_ID
fs.writeFile = () => {
assert.fail('should not be invoked')
}
const reporter = new HealthReporter(t.nr)
reporter.setStatus(HealthReporter.STATUS_AGENT_SHUTDOWN)
reporter.stop(() => {
assert.ok('stopped')
end()
})
})

test('setStatus warns for bad code', (t) => {
const reporter = new HealthReporter(t.nr)
reporter.setStatus('bad-code')
Expand Down

0 comments on commit 924e36d

Please sign in to comment.