Feature/add auditing monkeypatch (#28)
Feature/add auditing monkeypatch
This commit is contained in:
Родитель
069486b722
Коммит
798431c0f1
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"name": "Attach",
|
||||
"port": 9229
|
||||
},
|
||||
]
|
||||
}
|
19
README.md
19
README.md
|
@ -1,7 +1,7 @@
|
|||
# platform-chaos
|
||||
|
||||
|
||||
[![Build Status](https://travis-ci.org/azure/platform-chaos.svg?branch=master)](https://travis-ci.org/azure/platform-chaos)
|
||||
[![Build Status](https://travis-ci.org/Azure/platform-chaos.svg?branch=master)](https://travis-ci.org/Azure/platform-chaos)
|
||||
|
||||
A node sdk for building services capable of injecting chaos into PaaS offerings. ⚙️ 🌩
|
||||
|
||||
|
@ -83,6 +83,23 @@ Inflates the `resources` from a `req` objects `body` into a collection of object
|
|||
const objs = require('azure-chaos-fn/parsers').resourcesToObjects(req)
|
||||
```
|
||||
|
||||
### auditer
|
||||
|
||||
A documented implementation of the verbose logging format defined in [Auditing](https://github.com/Azure/platform-chaos/wiki/Auditing). The auditer is implemented by monkeypatching the `context` instance _log_ and _done_ methods. As a developer there is little extra effort you need to do to start using the auditer. At the beginning of your extension file, initialize the auditer by passing in the eventName and resources. Then use `context.log` as usual. Everything you log will be added to an intern audit list. When you call `context.done` at the end of your extension, the internal audit list is appended to the `context.res.body` under the `__audits` property.
|
||||
|
||||
Initialize the auditer by using the following method:
|
||||
```js
|
||||
const index = require('azure-chaos-fn')
|
||||
|
||||
index.auditer(/* Azure Function context */, {
|
||||
eventName: /* Chaos event name : string */,
|
||||
resources: /* Target resources : string */
|
||||
})
|
||||
```
|
||||
|
||||
See a fully implemented example in [this]() chaos event.
|
||||
> TODO: Add an example project for 'this'
|
||||
|
||||
## Related Projects
|
||||
|
||||
* [platform-chaos-api](https://github.com/Azure/platform-chaos-api) - An API for introducing chaos into Azure PaaS offerings using configurable extensions.
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
const shimmer = require('shimmer')
|
||||
const assert = require('assert')
|
||||
const os = require('os')
|
||||
const uuidv4 = require('uuid/v4')
|
||||
|
||||
const auditSystem = `${os.hostname()}-${os.platform()}`
|
||||
let auditQueue = []
|
||||
|
||||
const audit = (extensionLogArgs, auditOptions) => {
|
||||
const { eventName, resources } = auditOptions
|
||||
|
||||
auditQueue.push({
|
||||
auditId: uuidv4(),
|
||||
eventName: eventName,
|
||||
system: auditSystem,
|
||||
date: new Date().toISOString(),
|
||||
resources: resources,
|
||||
extensionLog: typeof extensionLogArgs === 'string' ? [ extensionLogArgs ] : Array.from(extensionLogArgs)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
audit: audit,
|
||||
initialize: (context, opts) => {
|
||||
assert(opts, 'Options object must be defined')
|
||||
assert(typeof opts.eventName === 'string', 'Event name must be a string')
|
||||
assert(typeof opts.resources === 'string', 'Resources must be a string')
|
||||
|
||||
const auditOptions = opts
|
||||
|
||||
auditQueue = []
|
||||
|
||||
shimmer.wrap(context, 'log', function (original) {
|
||||
return function () {
|
||||
audit(arguments, auditOptions)
|
||||
return original.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
|
||||
shimmer.wrap(context, 'done', function (original) {
|
||||
return function () {
|
||||
const audits = auditQueue
|
||||
|
||||
if (typeof context.res.body === 'undefined') {
|
||||
context.rex.body = {
|
||||
__audits: audits
|
||||
}
|
||||
} else if (typeof context.res.body === 'string') {
|
||||
const body = JSON.parse(context.res.body)
|
||||
body['__audits'] = audits
|
||||
context.res.body = JSON.stringify(body)
|
||||
} else if (typeof context.res.body === 'object') {
|
||||
context.res.body['__audits'] = audits
|
||||
}
|
||||
/*
|
||||
* If context.res.body is not of type string or object
|
||||
* do not append the audits list to it and return it
|
||||
* as usual.
|
||||
* */
|
||||
|
||||
return original.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
3
index.js
3
index.js
|
@ -1,4 +1,5 @@
|
|||
module.exports = {
|
||||
parsers: require('./parsers'),
|
||||
validators: require('./validators')
|
||||
validators: require('./validators'),
|
||||
auditer: require('./audit')
|
||||
}
|
||||
|
|
|
@ -4,6 +4,30 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.0.2.tgz",
|
||||
"integrity": "sha512-WR3dlgqJP4QNrLC4iXN/5/2WaLQQ0VijOOkmflqFGVJ6wLEpbSjo7c0ZeGIdtY8Crk7xBBp87sM6+Mkerz7alw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
},
|
||||
"@sinonjs/formatio": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
|
||||
"integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"samsam": "1.3.0"
|
||||
}
|
||||
},
|
||||
"@sinonjs/samsam": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.0.0.tgz",
|
||||
"integrity": "sha512-D7VxhADdZbDJ0HjUTMnSQ5xIGb4H2yWpg8k9Sf1T08zfFiQYlaxM8LZydpR4FQ2E6LZJX8IlabNZ5io4vdChwg==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz",
|
||||
|
@ -889,6 +913,12 @@
|
|||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
||||
"dev": true
|
||||
},
|
||||
"just-extend": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-3.0.0.tgz",
|
||||
"integrity": "sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==",
|
||||
"dev": true
|
||||
},
|
||||
"levn": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
|
||||
|
@ -935,6 +965,24 @@
|
|||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
|
||||
"dev": true
|
||||
},
|
||||
"lolex": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.4.tgz",
|
||||
"integrity": "sha512-Gh6Vffq/piTeHwunLNFR1jFVaqlwK9GMNUxFcsO1cwHyvbRKHwX8UDkxmrDnbcPdHNmpv7z2kxtkkSx5xkNpMw==",
|
||||
"dev": true
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
|
@ -1033,6 +1081,19 @@
|
|||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"nise": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.4.tgz",
|
||||
"integrity": "sha512-pxE0c9PzgrUTyhfv5p+5eMIdfU2bLEsq8VQEuE0kxM4zP7SujSar7rk9wpI2F7RyyCEvLyj5O7Is3RER5F36Fg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/formatio": "2.0.0",
|
||||
"just-extend": "3.0.0",
|
||||
"lolex": "2.7.4",
|
||||
"path-to-regexp": "1.7.0",
|
||||
"text-encoding": "0.6.4"
|
||||
}
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||
|
@ -1155,6 +1216,23 @@
|
|||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
|
||||
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isarray": "0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
|
||||
|
@ -1324,6 +1402,12 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"samsam": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
|
||||
"integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
|
@ -1345,12 +1429,45 @@
|
|||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
|
||||
"dev": true
|
||||
},
|
||||
"shimmer": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz",
|
||||
"integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag=="
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
|
||||
"dev": true
|
||||
},
|
||||
"sinon": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon/-/sinon-6.2.0.tgz",
|
||||
"integrity": "sha512-gLFZz5UYvOhYzQ+DBzw/OCkmWaLAHlAyQiE2wxUOmAGVdasP9Yw93E+OwZ0UuhW3ReMu1FKniuNsL6VukvC77w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sinonjs/commons": "1.0.2",
|
||||
"@sinonjs/formatio": "2.0.0",
|
||||
"@sinonjs/samsam": "2.0.0",
|
||||
"diff": "3.5.0",
|
||||
"lodash.get": "4.4.2",
|
||||
"lolex": "2.7.4",
|
||||
"nise": "1.4.4",
|
||||
"supports-color": "5.5.0",
|
||||
"type-detect": "4.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"slice-ansi": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
|
||||
|
@ -1463,6 +1580,12 @@
|
|||
"string-width": "2.1.1"
|
||||
}
|
||||
},
|
||||
"text-encoding": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
|
||||
"integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
|
||||
"dev": true
|
||||
},
|
||||
"text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
|
@ -1493,6 +1616,12 @@
|
|||
"prelude-ls": "1.1.2"
|
||||
}
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
||||
"dev": true
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
|
@ -1502,6 +1631,11 @@
|
|||
"punycode": "2.1.1"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^4.0.0",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"mocha": "^5.2.0"
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"mocha": "^5.2.0",
|
||||
"sinon": "^6.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"shimmer": "^1.2.0",
|
||||
"uuid": "^3.3.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
const assert = require('assert')
|
||||
const index = require('../')
|
||||
const sinon = require('sinon')
|
||||
const isEqual = require('lodash.isequal')
|
||||
|
||||
/* eslint-env node, mocha */
|
||||
|
||||
|
@ -97,4 +99,97 @@ describe('platform-chaos', () => {
|
|||
|
||||
assert.equal(res.headers['Authorization'], expectedAccessToken)
|
||||
})
|
||||
|
||||
it('audits correctly', () => {
|
||||
function contextLog () {
|
||||
// in reality this would be `console.log(...arguments)`
|
||||
// but in order to not cluter test we noop this
|
||||
return () => null
|
||||
}
|
||||
function contextDone () {
|
||||
return () => null
|
||||
}
|
||||
|
||||
const contextLogSpy = sinon.spy(contextLog)
|
||||
const contextDoneSpy = sinon.spy(contextDone)
|
||||
|
||||
const context = {
|
||||
log: contextLogSpy,
|
||||
done: contextDoneSpy,
|
||||
res: { body: {} }
|
||||
}
|
||||
|
||||
index.auditer.initialize(context, {
|
||||
eventName: 'testEvent',
|
||||
resources: 'testResource'
|
||||
})
|
||||
|
||||
const logItem1 = {
|
||||
'prop1': 'important information',
|
||||
'anotherProp': 'more important info'
|
||||
}
|
||||
const logItem2 = 'Hello, World!'
|
||||
const logItem3 = ['abc', { a: 12 }]
|
||||
|
||||
context.log(logItem1)
|
||||
context.log(logItem2)
|
||||
context.log(...logItem3)
|
||||
|
||||
context.res.body = {
|
||||
message: 'I am writing to the body'
|
||||
}
|
||||
|
||||
context.done()
|
||||
|
||||
assert(contextLogSpy.called, 'context.log should be called')
|
||||
assert(contextDoneSpy.called, 'context.done should be called')
|
||||
|
||||
const body = context.res.body
|
||||
|
||||
assert(typeof body === 'object', 'context.res.body should exist as an object')
|
||||
assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
|
||||
assert(isEqual(body.__audits[0].extensionLog[0], logItem1), 'log item 1 is added to audit correctly')
|
||||
assert(isEqual(body.__audits[1].extensionLog[0], logItem2), 'log item 2 is added to audit correctly')
|
||||
assert(isEqual(body.__audits[2].extensionLog, logItem3), 'log item 3 is added to audit correctly')
|
||||
})
|
||||
|
||||
it('allows user to audit directly', () => {
|
||||
function contextLog () {
|
||||
// in reality this would be `console.log(...arguments)`
|
||||
// but in order to not cluter test we noop this
|
||||
return () => null
|
||||
}
|
||||
function contextDone () {
|
||||
return () => null
|
||||
}
|
||||
|
||||
const contextLogSpy = sinon.spy(contextLog)
|
||||
const contextDoneSpy = sinon.spy(contextDone)
|
||||
|
||||
const context = {
|
||||
log: contextLogSpy,
|
||||
done: contextDoneSpy,
|
||||
res: { body: {} }
|
||||
}
|
||||
|
||||
const opts = {
|
||||
eventName: 'testEvent',
|
||||
resources: 'testResource'
|
||||
}
|
||||
|
||||
index.auditer.initialize(context, opts)
|
||||
|
||||
index.auditer.audit('Hello, World!', opts)
|
||||
|
||||
context.done()
|
||||
|
||||
assert(contextLogSpy.notCalled, 'context.log should not be called')
|
||||
assert(contextDoneSpy.called, 'context.done should be called')
|
||||
|
||||
const body = context.res.body
|
||||
|
||||
assert(typeof body === 'object', 'context.res.body should exist as an object')
|
||||
assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
|
||||
assert(body.__audits[0].extensionLog[0] === 'Hello, World!', 'log item 1 is added to audit correctly')
|
||||
})
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче