Skip to content

Commit

Permalink
Merge 4305097 into 10bc353
Browse files Browse the repository at this point in the history
  • Loading branch information
maguimarijuan committed Jul 29, 2020
2 parents 10bc353 + 4305097 commit dc6fade
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 65 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,14 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- serverlessConfiguration getter to easily get sls hooks.
- FirehoseInstance to cache the instance

## [3.2.0] - 2020-05-19
### Removed
- `package-lock.json` file

## [3.1.3] - 2020-05-15
### Fixed
- Unit tests improoves
- Unit tests improoves
- Errors that was not handled correctly

## [3.1.2] - 2020-05-12
Expand Down
37 changes: 29 additions & 8 deletions README.md
Expand Up @@ -12,13 +12,13 @@ npm install @janiscommerce/log

## Configuration
### ENV variables
**`JANIS_SERVICE_NAME`** (required): The name of the service that will create the log.
**`JANIS_SERVICE_NAME`** (required): The name of the service that will create the log.
**`JANIS_ENV`** (required): The stage name that will used as prefix for trace firehose delivery stream.
**`LOG_ROLE_ARN`** (required): The ARN to assume the trace role in order to put records in Firehose.

## API
### **`add(clientCode, logs)`**
Parameters: `clientCode [String]`, `logs [Object] or [Object array]`
### **`add(clientCode, logs)`**
Parameters: `clientCode [String]`, `logs [Object] or [Object array]`
Puts the recieved log or logs into the janis-trace-firehose

### Log structure
Expand Down Expand Up @@ -53,15 +53,15 @@ The `log [Object]` parameter have the following structure:
}
```

### **`on(event, callback)`**
### **`on(event, callback)`**
Parameters: `event [String]`, `callback [Function]`
Calls a callback when the specified event is emitted.

## Errors

The errors are informed with a `LogError`.
This object has a code that can be useful for a correct error handling.
The codes are the following:
The errors are informed with a `LogError`.
This object has a code that can be useful for a correct error handling.
The codes are the following:

| Code | Description |
|------|--------------------------------|
Expand Down Expand Up @@ -148,4 +148,25 @@ await Log.add('some-client', [

Log.on('create-error', (log, err) => {
console.error(`An error occurred while creating the log ${err.message}`);
});
});
```

### Serverless configuration

Returns an array with the hooks needed for Log's serverless configuration according to [Serverless Helper](https://www.npmjs.com/package/sls-helper-plugin-janis). In `path/to/root/serverless.js` add:

```js
'use strict';

const { helper } = require('sls-helper'); // eslint-disable-line
const functions = require('./serverless/functions.json');
const Log = require('@janiscommerce/log');

module.exports = helper({
hooks: [
// other hooks
...functions,
...Log.serverlessConfiguration
]
});
```
74 changes: 74 additions & 0 deletions lib/firehose-instance.js
@@ -0,0 +1,74 @@
'use strict';

const { STS, Firehose } = require('./aws-wrappers');
const LogError = require('./log-error');

const sts = new STS();
const ARN_DURATION = 1800; // 30 min
const MAX_TIMEOUT = 500;

class FirehoseInstance {

static get serviceName() {
return process.env.JANIS_SERVICE_NAME;
}

static get roleArn() {
return process.env.LOG_ROLE_ARN;
}

/**
* Returns a FirehoseInstance
*
* @returns {Object} A FireHoseInstance
*/
static async getFirehoseInstance() {

if(!this.validCredentials()) {


const firehoseParams = {
region: process.env.AWS_DEFAULT_REGION,
httpOptions: { timeout: MAX_TIMEOUT }
};

if(this.roleArn) {
firehoseParams.credentials = await this.getCredentials();
this.credentialsExpiration = new Date(firehoseParams.credentials.expiration);
}

this.firehose = new Firehose(firehoseParams);
}

return this.firehose;
}

static validCredentials() {
return this.firehose
&& this.credentialsExpiration
&& this.credentialsExpiration >= new Date();
}

static async getCredentials() {

const assumedRole = await sts.assumeRole({
RoleArn: this.roleArn,
RoleSessionName: this.serviceName,
DurationSeconds: ARN_DURATION
});

if(!assumedRole)
throw new LogError('Failed to assume role, invalid response.', LogError.codes.ASSUME_ROLE_ERROR);

const { Credentials, Expiration } = assumedRole;

return {
accessKeyId: Credentials.AccessKeyId,
secretAccessKey: Credentials.SecretAccessKey,
sessionToken: Credentials.SessionToken,
expiration: Expiration
};
}
}

module.exports = FirehoseInstance;
69 changes: 13 additions & 56 deletions lib/log.js
@@ -1,20 +1,17 @@
'use strict';

const EventEmmiter = require('events');

const { STS, Firehose } = require('./aws-wrappers');
const { arrayChunk } = require('./helpers/utils');

const Validator = require('./helpers/validator');
const LogError = require('./log-error');
const serverlessConfiguration = require('./serverless-configuration');
const FirehoseInstance = require('./firehose-instance');


const ARN_DURATION = 1800; // 30 min
const MAX_ATTEMPTS = 3;
const MAX_TIMEOUT = 500;
const DELIVERY_STREAM_PREFIX = 'JanisTraceFirehose';
const LOGS_BATCH_LIMIT = 500;

const sts = new STS();

const emitter = new EventEmmiter();

Expand All @@ -28,10 +25,6 @@ class Log {
return process.env.JANIS_ENV;
}

static get roleArn() {
return process.env.LOG_ROLE_ARN;
}

static get envs() {

return {
Expand Down Expand Up @@ -119,7 +112,7 @@ class Log {

try {

const firehose = await this.getFirehoseInstance();
const firehose = await FirehoseInstance.getFirehoseInstance();

await Promise.all(
logsBatches.map(logs => firehose.putRecordBatch(
Expand All @@ -145,51 +138,15 @@ class Log {
}
}

static async getFirehoseInstance() {

if(!this.validCredentials()) {

const firehoseParams = {
region: process.env.AWS_DEFAULT_REGION,
httpOptions: { timeout: MAX_TIMEOUT }
};

if(this.roleArn) {
firehoseParams.credentials = await this.getCredentials();
this.credentialsExpiration = new Date(firehoseParams.credentials.expiration);
}

this.firehose = new Firehose(firehoseParams);
}

return this.firehose;
}

static validCredentials() {
return this.firehose
&& this.credentialsExpiration
&& this.credentialsExpiration >= new Date();
}

static async getCredentials() {

const assumedRole = await sts.assumeRole({
RoleArn: this.roleArn,
RoleSessionName: this.serviceName,
DurationSeconds: ARN_DURATION
});

if(!assumedRole)
throw new LogError('Failed to assume role, invalid response.', LogError.codes.ASSUME_ROLE_ERROR);

const { Credentials, Expiration } = assumedRole;

return {
accessKeyId: Credentials.AccessKeyId,
secretAccessKey: Credentials.SecretAccessKey,
sessionToken: Credentials.SessionToken,
expiration: Expiration
};
/**
* Returns the sls helpers needed for serverles configuration.
*
* @readonly
* @static
* @memberof Log
*/
static get serverlessConfiguration() {
return serverlessConfiguration();
}
}

Expand Down
18 changes: 18 additions & 0 deletions lib/serverless-configuration.js
@@ -0,0 +1,18 @@
'use strict';

const Settings = require('@janiscommerce/settings');

module.exports = () => {

const logRoleArn = Settings.get('logRoleArn');

return [
['envVars', {
LOG_ROLE_ARN: logRoleArn
}],
['iamStatement', {
action: 'Sts:AssumeRole',
resource: logRoleArn
}]
];
};
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -34,6 +34,7 @@
"test": "tests"
},
"dependencies": {
"@janiscommerce/settings": "^1.0.1",
"@janiscommerce/superstruct": "^1.1.1",
"aws-sdk": "^2.498.0",
"uuid": "7.0.3"
Expand Down
21 changes: 21 additions & 0 deletions tests/log-test.js
Expand Up @@ -2,6 +2,7 @@

const assert = require('assert');
const sandbox = require('sinon').createSandbox();
const Settings = require('@janiscommerce/settings');

const { STS, Firehose } = require('../lib/aws-wrappers');

Expand Down Expand Up @@ -362,5 +363,25 @@ describe('Log', () => {
});
});
});

});

context('Serverless configuration getter', () => {

it('Should return the serverless hooks', () => {

sandbox.stub(Settings, 'get').returns('logArnSource');

assert.deepStrictEqual(Log.serverlessConfiguration, [
['envVars', {
LOG_ROLE_ARN: 'logArnSource'
}], ['iamStatement', {
action: 'Sts:AssumeRole',
resource: 'logArnSource'
}]
]);

sandbox.assert.calledOnceWithExactly(Settings.get, 'logRoleArn');
});
});
});

0 comments on commit dc6fade

Please sign in to comment.