Skip to content
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.

refactor(server): remove separate notifier process #1800

Merged
merged 2 commits into from
Apr 20, 2017
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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ This runs a script `scripts/start-local.sh` as defined in `package.json`. This w
* `bin/key_server.js` on port 9000
* `test/mail_helper.js` on port 9001
* `./node_modules/fxa-customs-server/bin/customs_server.js` on port 7000
* `bin/notifier.js` (no port)

When you `Ctrl-c` your server, all 4 processes will be stopped.

Expand Down
5 changes: 4 additions & 1 deletion bin/key_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ function run(config) {
v && v.constructor === RegExp ? v.toString() : v
)

log.stdout.write('{"event":"config","data":' + stringifiedConfig + '}\n')
log.notifier.send({
event: 'config',
data: config
})

if (config.env !== 'prod') {
log.info(stringifiedConfig, 'starting config')
Expand Down
66 changes: 0 additions & 66 deletions bin/notifier.js

This file was deleted.

2 changes: 1 addition & 1 deletion docs/self-host.docker
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ USER fxa
RUN npm install --production \
&& npm cache clear

CMD node ./bin/key_server.js | node ./bin/notifier.js
CMD node ./bin/key_server.js

# Expose ports
EXPOSE 9000
9 changes: 5 additions & 4 deletions lib/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const logConfig = config.get('log')
const StatsDCollector = require('./metrics/statsd')
const unbuffer = require('./crypto/butil').unbuffer


function Lug(options) {
EventEmitter.call(this)
this.name = options.name || 'fxa-auth-server'
Expand All @@ -27,6 +28,8 @@ function Lug(options) {

this.statsd = new StatsDCollector(this.logger)
this.statsd.init()

this.notifier = require('./notifier')(this)
}
util.inherits(Lug, EventEmitter)

Expand Down Expand Up @@ -138,9 +141,7 @@ Lug.prototype.summary = function (request, response) {


// Broadcast an event to attached services, such as sync.
// In production, these events are read from stdout
// and broadcast to relying services over SNS/SQS.

// In production, these events are broadcast to relying services over SNS/SQS.
Lug.prototype.notifyAttachedServices = function (name, request, data) {
return request.gatherMetricsContext({})
.then(
Expand All @@ -150,7 +151,7 @@ Lug.prototype.notifyAttachedServices = function (name, request, data) {
data: unbuffer(data)
}
e.data.metricsContext = metricsContextData
this.stdout.write(JSON.stringify(e) + '\n')
this.notifier.send(e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um, yes please!

}
)
}
Expand Down
55 changes: 55 additions & 0 deletions lib/notifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

/**
* This notifier is called by the logger via `notifyAttachedServices`
* to send notifications to Amazon SNS/SQS.
*/
const AWS = require('aws-sdk')
const config = require('../config')

const notifierSnsTopicArn = config.get('snsTopicArn')
let sns = { publish: function (msg, cb) {
cb(null, {disabled: true})
}}

if (notifierSnsTopicArn !== 'disabled') {
// Pull the region info out of the topic arn.
// For some reason we need to pass this in explicitly.
// Format is "arn:aws:sns:<region>:<other junk>"
const region = notifierSnsTopicArn.split(':')[3]
// This will pull in default credentials, region data etc
// from the metadata service available to the instance.
// It's magic, and it's awesome.
sns = new AWS.SNS({region: region})
}

module.exports = function notifierLog(log) {
return {
send: (event, callback) => {
const msg = event.data || {}
msg.event = event.event

sns.publish({
TopicArn: notifierSnsTopicArn,
Message: JSON.stringify(msg)
}, (err, data) => {
if (err) {
log.error({op: 'Notifier.publish', err: err})
} else {
log.trace({op: 'Notifier.publish', success: true, data: data})
}

if (callback) {
callback(err, data)
}
})

},
// exported for testing purposes
__sns: sns
}
}
2 changes: 1 addition & 1 deletion scripts/start-local-mysql.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ node ./node_modules/fxa-auth-db-mysql/bin/db_patcher.js
node ./node_modules/fxa-auth-db-mysql/bin/server.js &
DB=$!

node ./bin/key_server.js | node ./bin/notifier.js >/dev/null
node ./bin/key_server.js

kill $MH
kill $DB
2 changes: 1 addition & 1 deletion scripts/start-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ MH=$!
node ./node_modules/fxa-auth-db-mysql/bin/mem.js &
DB=$!

node ./bin/key_server.js | node ./bin/notifier.js >/dev/null
node ./bin/key_server.js

kill $MH
kill $DB
2 changes: 1 addition & 1 deletion scripts/start-server.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
node ./bin/key_server.js | node ./bin/notifier.js >/dev/null
node ./bin/key_server.js
exit $PIPESTATUS[0]
1 change: 1 addition & 0 deletions test/local/ip_profiling.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ describe('IP Profiling', () => {
})

mockRequest.app = {
clientAddress: '63.245.221.32',
isSuspiciousRequest: true
}

Expand Down
86 changes: 86 additions & 0 deletions test/local/notifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

const ROOT_DIR = '../..'

const proxyquire = require('proxyquire')
const assert = require('insist')
const sinon = require('sinon')

describe('notifier', () => {
const log = {
error: sinon.spy(),
trace: sinon.spy()
}

beforeEach(() => {
log.error.reset()
log.trace.reset()
})

it('works with sns configuration', () => {
const config = {
get: (key) => {
if (key === 'snsTopicArn') {
return 'arn:aws:sns:us-west-2:927034868275:foo'
}
}
}

const notifier = proxyquire(`${ROOT_DIR}/lib/notifier`, {
'../config': config
})(log)

notifier.__sns.publish = sinon.spy((event, cb) => {
cb(null, event)
})

notifier.send({
event: {
stuff: true
}
})

assert.deepEqual(log.trace.args[0][0], {
op: 'Notifier.publish',
data: {
TopicArn: 'arn:aws:sns:us-west-2:927034868275:foo',
Message: '{\"event\":{\"stuff\":true}}'
},
success: true
})
assert.equal(log.error.called, false)
})

it('works with disabled configuration', () => {
const config = {
get: (key) => {
if (key === 'snsTopicArn') {
return 'disabled'
}
}
}
const notifier = proxyquire(`${ROOT_DIR}/lib/notifier`, {
'../config': config
})(log)

notifier.send({
stuff: true
}, () => {
assert.deepEqual(log.trace.args[0][0], {
op: 'Notifier.publish',
data: {
disabled: true
},
success: true
})
assert.equal(log.trace.args[0][0].data.disabled, true)
assert.equal(log.error.called, false)
})

})

})
36 changes: 9 additions & 27 deletions test/local/routes/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,24 +178,16 @@ describe('/account/reset', function () {

describe('/account/create', () => {
it('should create an account', () => {
// We want to test what's actually written to stdout by the logger.
const mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
const mockLog = log('ERROR', 'test')
mockLog.activityEvent = sinon.spy(() => {
return P.resolve()
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
mockLog.error = sinon.spy()
mockLog.notifier.send = sinon.spy()

const mockMetricsContext = mocks.mockMetricsContext()
const mockRequest = mocks.mockRequest({
locale: 'en-GB',
Expand Down Expand Up @@ -266,8 +258,8 @@ describe('/account/create', () => {
return runTest(route, mockRequest, () => {
assert.equal(mockDB.createAccount.callCount, 1, 'createAccount was called')

assert.equal(mockLog.stdout.write.callCount, 1, 'an sqs event was logged')
var eventData = JSON.parse(mockLog.stdout.write.getCall(0).args[0])
assert.equal(mockLog.notifier.send.callCount, 1, 'an sqs event was logged')
var eventData = mockLog.notifier.send.getCall(0).args[0]
assert.equal(eventData.event, 'login', 'it was a login event')
assert.equal(eventData.data.service, 'sync', 'it was for sync')
assert.equal(eventData.data.email, TEST_EMAIL, 'it was for the correct email')
Expand Down Expand Up @@ -363,23 +355,14 @@ describe('/account/login', function () {
codeLifetime: 1000
}
}
// We want to test what's actually written to stdout by the logger.
const mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
const mockLog = log('ERROR', 'test')
mockLog.activityEvent = sinon.spy(() => {
return P.resolve()
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
mockLog.notifier.send = sinon.spy()
const mockMetricsContext = mocks.mockMetricsContext()

const mockRequest = mocks.mockRequest({
Expand Down Expand Up @@ -470,7 +453,6 @@ describe('/account/login', function () {
afterEach(() => {
mockLog.activityEvent.reset()
mockLog.flowEvent.reset()
mockLog.stdout.write.reset()
mockMailer.sendNewDeviceLoginNotification = sinon.spy(() => P.resolve([]))
mockMailer.sendVerifyLoginEmail.reset()
mockMailer.sendVerifyCode.reset()
Expand All @@ -492,8 +474,8 @@ describe('/account/login', function () {
assert.equal(mockDB.emailRecord.callCount, 1, 'db.emailRecord was called')
assert.equal(mockDB.createSessionToken.callCount, 1, 'db.createSessionToken was called')

assert.equal(mockLog.stdout.write.callCount, 1, 'an sqs event was logged')
var eventData = JSON.parse(mockLog.stdout.write.getCall(0).args[0])
assert.equal(mockLog.notifier.send.callCount, 1, 'an sqs event was logged')
var eventData = mockLog.notifier.send.getCall(0).args[0]
assert.equal(eventData.event, 'login', 'it was a login event')
assert.equal(eventData.data.service, 'sync', 'it was for sync')
assert.equal(eventData.data.email, TEST_EMAIL, 'it was for the correct email')
Expand Down