Skip to content

Commit

Permalink
update for latest spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Jan 22, 2025
1 parent c817ebf commit f093ad4
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 30 deletions.
2 changes: 1 addition & 1 deletion lib/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ const DEFAULT_HARVEST_INTERVAL_MS = 60000
function Agent(config) {
EventEmitter.call(this)

this.healthReporter = new HealthReporter()
this.healthReporter = new HealthReporter({ agentConfig: config })

if (!config) {
this.healthReporter.setStatus(HealthReporter.STATUS_CONFIG_PARSE_FAILURE)

Check warning on line 165 in lib/agent.js

View check run for this annotation

Codecov / codecov/patch

lib/agent.js#L165

Added line #L165 was not covered by tests
Expand Down
13 changes: 7 additions & 6 deletions lib/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,13 @@ defaultConfig.definition = () => ({
*/
agent_control: {
/**
* If set, must be a GUID string. Indicates that the agent is being managed
* by Agent Control. Must be set to enable health monitoring.
* Indicates that the agent is being managed by Agent Control. Must be set
* to true health monitoring.
*/
fleet_id: {
env: 'NEW_RELIC_AGENT_CONTROL_FLEET_ID',
default: null
enabled: {
env: 'NEW_RELIC_AGENT_CONTROL_ENABLED',
formatter: boolean,
default: false
},

/**
Expand All @@ -173,7 +174,7 @@ defaultConfig.definition = () => ({
*/
delivery_location: {
env: 'NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION',
default: null
default: 'file:///var/lib/newrelic-agent_control/fleet'
},

/**
Expand Down
29 changes: 26 additions & 3 deletions lib/health-reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ function writeStatus({ file, healthy = true, code, msg, startTime, callback } =
fs.writeFile(file, yaml, { encoding: 'utf8' }, callback)
}

Check warning on line 41 in lib/health-reporter.js

View check run for this annotation

Codecov / codecov/patch

lib/health-reporter.js#L31-L41

Added lines #L31 - L41 were not covered by tests

function directoryAvailable(dest) {
try {
fs.accessSync(dest, fs.constants.R_OK | fs.constants.W_OK)
return { available: true }
} catch (error) {
return { available: false, error }
}
}

Check warning on line 50 in lib/health-reporter.js

View check run for this annotation

Codecov / codecov/patch

lib/health-reporter.js#L43-L50

Added lines #L43 - L50 were not covered by tests

/**
* HealthReporter implements the "super agent" (New Relic Control) health
* check spec. An instance of the reporter will continually write out the
Expand Down Expand Up @@ -75,14 +84,14 @@ class HealthReporter {
logger = defaultLogger,
setInterval = global.setInterval
} = {}) {
const fleetId = agentConfig.agent_control?.fleet_id
const enabled = agentConfig.agent_control?.enabled
const outDir = agentConfig.agent_control?.health?.delivery_location
let checkInterval = agentConfig.agent_control?.health?.frequency

this.#logger = logger

if (!fleetId) {
this.#logger.info('new relic control not present, skipping health reporting')
if (enabled !== true) {
this.#logger.info('new relic agent control disabled, skipping health reporting')
return
}

Expand All @@ -91,6 +100,12 @@ class HealthReporter {
return
}

const dirCheck = directoryAvailable(outDir)
if (dirCheck.available === false) {
this.#logger.error('health check output directory not accessible, skipping health reporting', { error: dirCheck.error })
return
}

if (checkInterval === undefined) {
this.#logger.debug('health check interval not available, using default 5 seconds')
checkInterval = 5_000
Expand Down Expand Up @@ -131,6 +146,14 @@ class HealthReporter {
})
}

Check warning on line 147 in lib/health-reporter.js

View check run for this annotation

Codecov / codecov/patch

lib/health-reporter.js#L134-L147

Added lines #L134 - L147 were not covered by tests

get enabled() {
return this.#enabled
}

Check warning on line 151 in lib/health-reporter.js

View check run for this annotation

Codecov / codecov/patch

lib/health-reporter.js#L150-L151

Added lines #L150 - L151 were not covered by tests

get destFile() {
return this.#destFile
}

Check warning on line 155 in lib/health-reporter.js

View check run for this annotation

Codecov / codecov/patch

lib/health-reporter.js#L154-L155

Added lines #L154 - L155 were not covered by tests

/**
* Update the known health status. This status will be written to the health
* file on the next interval. If the provided status is not a recognized
Expand Down
32 changes: 24 additions & 8 deletions test/unit/agent/agent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@ 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
process.env.NEW_RELIC_AGENT_CONTROL_ENABLED = 'true'
process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION = os.tmpdir()
process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY = 1
const HealthReporter = require('#agentlib/health-reporter.js')

test.after(() => {
const files = fs.readdirSync(process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION)
const files = fs.readdirSync(process.env.NEW_RELIC_AGENT_CONTROL_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), {
fs.rmSync(path.join(process.env.NEW_RELIC_AGENT_CONTROL_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
delete process.env.NEW_RELIC_AGENT_CONTROL_ENABLED
delete process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION
delete process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY
})

const RUN_ID = 1337
Expand Down Expand Up @@ -70,6 +70,22 @@ test('should not throw with valid config', () => {
assert.equal(agent.config.agent_enabled, false)
})

test('should initialize health reporter', () => {
const dest = os.tmpdir()
const config = configurator.initialize({
agent_enabled: false,
agent_control: {
enabled: true,
health: {
delivery_location: dest
}
}
})
const agent = new Agent(config)
assert.equal(agent.healthReporter.enabled, true)
assert.equal(agent.healthReporter.destFile.startsWith(dest), true)
})

test('when loaded with defaults', async (t) => {
t.beforeEach((ctx) => {
ctx.nr = {}
Expand Down
14 changes: 7 additions & 7 deletions test/unit/config/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,24 +89,24 @@ test('agent control', async t => {
await t.test('loads defaults', () => {
const config = Config.initialize({})
assert.deepStrictEqual(config.agent_control, {
fleet_id: null,
enabled: false,
health: {
delivery_location: null,
delivery_location: 'file:///var/lib/newrelic-agent_control/fleet',
frequency: 5
}
})
})

await t.test('loads from env', () => {
process.env.NEW_RELIC_AGENT_CONTROL_FLEET_ID = 42
process.env.NEW_RELIC_AGENT_CONTROL_ENABLED = 'true'
process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION = 'file://find/me'
process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY = 1
const config = Config.initialize({})
delete process.env.NEW_RELIC_AGENT_CONTROL_FLEET_ID
delete process.env.NEW_RELIC_AGENT_CONTROL_ENABLED
delete process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION
delete process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY
assert.deepStrictEqual(config.agent_control, {
fleet_id: '42',
enabled: true,
health: {
delivery_location: 'file://find/me',
frequency: 1
Expand All @@ -117,15 +117,15 @@ test('agent control', async t => {
await t.test('loads from provided config', () => {
const config = Config.initialize({
agent_control: {
fleet_id: 'from-config',
enabled: true,
health: {
delivery_location: 'file://find/me',
frequency: 10
}
}
})
assert.deepStrictEqual(config.agent_control, {
fleet_id: 'from-config',
enabled: true,
health: {
delivery_location: 'file://find/me',
frequency: 10
Expand Down
30 changes: 25 additions & 5 deletions test/unit/lib/health-reporter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ function simpleInterval(method) {

test.beforeEach((ctx) => {
ctx.nr = {}
ctx.nr.accessOrigin = fs.accessSync
ctx.nr.writeFileOrig = fs.writeFile
ctx.nr.bigintOrig = process.hrtime.bigint

fs.accessSync = () => true

let count = 0n
process.hrtime.bigint = () => {
count += 1n
Expand Down Expand Up @@ -57,7 +60,7 @@ test.beforeEach((ctx) => {

ctx.nr.agentConfig = Config.initialize({
agent_control: {
fleet_id: 42,
enabled: true,
health: {
delivery_location: os.tmpdir(),
frequency: 1
Expand All @@ -67,20 +70,21 @@ test.beforeEach((ctx) => {
})

test.afterEach((ctx) => {
fs.accessSync = ctx.nr.accessOrig
fs.writeFile = ctx.nr.writeFileOrig
process.hrtime.bigint = ctx.nr.bigintOrig
})

test('requires fleet id to be set', (t) => {
delete t.nr.agentConfig.agent_control.fleet_id
test('requires enabled to be true', (t) => {
delete t.nr.agentConfig.agent_control.enabled

const reporter = new HealthReporter(t.nr)
assert.ok(reporter)

const {
logs: { info }
} = t.nr
assert.deepStrictEqual(info, [['new relic control not present, skipping health reporting']])
assert.deepStrictEqual(info, [['new relic agent control disabled, skipping health reporting']])
})

test('requires output directory to be set', (t) => {
Expand All @@ -98,6 +102,22 @@ test('requires output directory to be set', (t) => {
])
})

test('requires output directory to readable and writable', (t) => {
fs.accessSync = () => {
throw Error('boom')
}

const reporter = new HealthReporter(t.nr)
assert.ok(reporter)

const {
logs: { info, error }
} = t.nr
assert.equal(info.length, 0, 'should not log any info messages')
assert.deepStrictEqual(error[0][0], 'health check output directory not accessible, skipping health reporting')
assert.equal(error[0][1].error.message, 'boom')
})

test('sets default interval', (t) => {
delete t.nr.agentConfig.agent_control.health.frequency

Expand Down Expand Up @@ -174,7 +194,7 @@ test('logs error if writing failed', async (t) => {
})

test('setStatus and stop do nothing if reporter disabled', (t, end) => {
delete t.nr.agentConfig.agent_control.fleet_id
delete t.nr.agentConfig.agent_control.enabled
fs.writeFile = () => {
assert.fail('should not be invoked')
}
Expand Down

0 comments on commit f093ad4

Please sign in to comment.