From f2b460bac9e757a189066d953904a44aceb520ad Mon Sep 17 00:00:00 2001 From: James Sumners Date: Wed, 22 Jan 2025 12:52:38 -0500 Subject: [PATCH] update for latest spec --- lib/config/default.js | 13 ++++++------ lib/health-reporter.js | 21 ++++++++++++++++--- test/unit/config/config.test.js | 14 ++++++------- test/unit/lib/health-reporter.test.js | 30 ++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/lib/config/default.js b/lib/config/default.js index 228a8e41ee..1802fea91f 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -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 }, /** @@ -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' }, /** diff --git a/lib/health-reporter.js b/lib/health-reporter.js index ea6fd557b3..5e731b8884 100644 --- a/lib/health-reporter.js +++ b/lib/health-reporter.js @@ -40,6 +40,15 @@ function writeStatus({ file, healthy = true, code, msg, startTime, callback } = fs.writeFile(file, yaml, { encoding: 'utf8' }, callback) } +function directoryAvailable(dest) { + try { + fs.accessSync(dest, fs.constants.R_OK | fs.constants.W_OK) + return { available: true } + } catch (error) { + return { available: false, error } + } +} + /** * HealthReporter implements the "super agent" (New Relic Control) health * check spec. An instance of the reporter will continually write out the @@ -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 } @@ -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 diff --git a/test/unit/config/config.test.js b/test/unit/config/config.test.js index fa5bfa15dc..53ae34f4c6 100644 --- a/test/unit/config/config.test.js +++ b/test/unit/config/config.test.js @@ -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 @@ -117,7 +117,7 @@ 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 @@ -125,7 +125,7 @@ test('agent control', async t => { } }) assert.deepStrictEqual(config.agent_control, { - fleet_id: 'from-config', + enabled: true, health: { delivery_location: 'file://find/me', frequency: 10 diff --git a/test/unit/lib/health-reporter.test.js b/test/unit/lib/health-reporter.test.js index c2160b0dd9..6ccf2183b3 100644 --- a/test/unit/lib/health-reporter.test.js +++ b/test/unit/lib/health-reporter.test.js @@ -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 @@ -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 @@ -67,12 +70,13 @@ 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) @@ -80,7 +84,7 @@ test('requires fleet id to be set', (t) => { 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) => { @@ -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 @@ -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') }