Skip to content

Commit

Permalink
implement configuration support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Jan 13, 2025
1 parent 6170707 commit 79b116c
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 17 deletions.
41 changes: 41 additions & 0 deletions lib/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,47 @@ defaultConfig.definition = () => ({
formatter: boolean,
default: true
},

/**
* Collects configuration related to New Relic Agent Control, i.e. centralized
* agent management in container based environments.
*/
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.
*/
fleet_id: {
env: 'NEW_RELIC_AGENT_CONTROL_FLEET_ID',
default: null
},

/**
* Settings specific to the health monitoring aspect of Agent Control.
*/
health: {
/**
* A string file path to a directory that the agent is expected to write
* health status files to. Must be set for health monitoring to be
* enabled.
*/
delivery_location: {
env: 'NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION',
default: null
},

/**
* The time, in seconds, that the agent should wait between writing
* updates to its health status. The default interval is 5 seconds.
*/
frequency: {
env: 'NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY',
formatter: int,
default: 5
}
}
},

/**
* The default Apdex tolerating / threshold value for applications, in
* seconds. The default for Node is apdexT to 100 milliseconds, which is
Expand Down
15 changes: 10 additions & 5 deletions lib/health-reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,14 @@ class HealthReporter {
// STATUS_INTERNAL errors are the Node.js Agent specific error codes.
static STATUS_INTERNAL_UNEXPECTED_ERROR = 'NR-APM-300'

constructor({ logger = defaultLogger, setInterval = global.setInterval } = {}) {
const fleetId = process.env.NEW_RELIC_SUPERAGENT_FLEET_ID
const outDir = process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION
let checkInterval = process.env.NEW_RELIC_SUPERAGENT_HEALTH_FREQUENCY
constructor({
agentConfig = { agent_control: { health: {} } },
logger = defaultLogger,
setInterval = global.setInterval
} = {}) {
const fleetId = agentConfig.agent_control?.fleet_id
const outDir = agentConfig.agent_control?.health?.delivery_location
let checkInterval = agentConfig.agent_control?.health?.frequency

this.#logger = logger

Expand Down Expand Up @@ -168,7 +172,8 @@ class HealthReporter {
*/
stop(done) {
if (this.#enabled === false) {
return done()
done && done()
return
}

clearInterval(this.#interval)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@
"newrelic-naming-rules": "./bin/test-naming-rules.js"
},
"imports": {
"#agentlib/*.js": "./lib/*.js"
"#agentlib/*.js": "./lib/*.js",
"#test/assert": "./test/lib/custom-assertions/index.js"
},
"dependencies": {
"@grpc/grpc-js": "^1.12.2",
Expand Down
49 changes: 49 additions & 0 deletions test/unit/config/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,55 @@ test('when loading options via constructor', async (t) => {
})
})

test('agent control', async t => {
await t.test('loads defaults', () => {
const config = Config.initialize({})
assert.deepStrictEqual(config.agent_control, {
fleet_id: null,
health: {
delivery_location: null,
frequency: 5
}
})
})

await t.test('loads from env', () => {
process.env.NEW_RELIC_AGENT_CONTROL_FLEET_ID = 42
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_HEALTH_DELIVERY_LOCATION
delete process.env.NEW_RELIC_AGENT_CONTROL_HEALTH_FREQUENCY
assert.deepStrictEqual(config.agent_control, {
fleet_id: '42',
health: {
delivery_location: 'file://find/me',
frequency: 1
}
})
})

await t.test('loads from provided config', () => {
const config = Config.initialize({
agent_control: {
fleet_id: 'from-config',
health: {
delivery_location: 'file://find/me',
frequency: 10
}
}
})
assert.deepStrictEqual(config.agent_control, {
fleet_id: 'from-config',
health: {
delivery_location: 'file://find/me',
frequency: 10
}
})
})
})

test('#publicSettings', async (t) => {
let configuration

Expand Down
26 changes: 15 additions & 11 deletions test/unit/lib/health-reporter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const os = require('node:os')
const fs = require('node:fs')
const tspl = require('@matteo.collina/tspl')

const match = require('../../lib/custom-assertions/match')
const { match } = require('#test/assert')
const Config = require('#agentlib/config/index.js')
const HealthReporter = require('#agentlib/health-reporter.js')

function simpleInterval(method) {
Expand Down Expand Up @@ -54,21 +55,24 @@ test.beforeEach((ctx) => {
}
}

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
ctx.nr.agentConfig = Config.initialize({
agent_control: {
fleet_id: 42,
health: {
delivery_location: os.tmpdir(),
frequency: 1
}
}
})
})

test.afterEach((ctx) => {
fs.writeFile = ctx.nr.writeFileOrig
process.hrtime.bigint = ctx.nr.bigintOrig
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
})

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

const reporter = new HealthReporter(t.nr)
assert.ok(reporter)
Expand All @@ -80,7 +84,7 @@ test('requires fleet id to be set', (t) => {
})

test('requires output directory to be set', (t) => {
delete process.env.NEW_RELIC_SUPERAGENT_HEALTH_DELIVERY_LOCATION
delete t.nr.agentConfig.agent_control.health.delivery_location

const reporter = new HealthReporter(t.nr)
assert.ok(reporter)
Expand All @@ -95,7 +99,7 @@ test('requires output directory to be set', (t) => {
})

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

const reporter = new HealthReporter(t.nr)
assert.ok(reporter)
Expand Down Expand Up @@ -170,7 +174,7 @@ 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
delete t.nr.agentConfig.agent_control.fleet_id
fs.writeFile = () => {
assert.fail('should not be invoked')
}
Expand Down

0 comments on commit 79b116c

Please sign in to comment.