Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eavesdocker

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eavesdocker - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

lib/pipeline.js

12

CHANGELOG.md

@@ -8,2 +8,13 @@ # Eavesdocker Changelog

## [v0.3.0] - 2021-08-20
### Added
- twilio transport to send SMS
- support to yml config files
- templating to GitLab `title` and `labels` settings
- templating to email `subject` setting
### Changed
- entire configuration schema from `tasks` to `pipeline` key
## [v0.2.0] - 2021-08-18

@@ -143,1 +154,2 @@

[v0.2.0]: https://gitlab.com/GCSBOSS/eavesdocker/-/tags/v0.2.0
[v0.3.0]: https://gitlab.com/GCSBOSS/eavesdocker/-/tags/v0.3.0

10

lib/container.js
const { routeMessage } = require('./transport');
const { matchTasks } = require('./task');
const { shouldAttachContainer } = require('./pipeline');

@@ -26,4 +26,3 @@ const os = require('os');

c.tasks = matchTasks.call(this, c);
if(c.tasks == 0)
if(!shouldAttachContainer.call(this, c))
return;

@@ -37,3 +36,3 @@

this.log.debug('Attached to %s with %s tasks', c.cid, c.tasks.length);
this.log.debug('Attached to %s', c.cid);

@@ -45,3 +44,2 @@ if(this.global.healthRedis)

catch(_err){
// console.log(err);
this.log.warn('Failed to attach to %s', c.cid);

@@ -57,3 +55,3 @@ }

this.global.containers[id].stream.destroy();
this.log.debug('Removed container %s', this.global.containers[id].cid);
this.log.debug('Dettached from %s', this.global.containers[id].cid);
delete this.global.containers[id];

@@ -60,0 +58,0 @@

29

lib/main.js

@@ -5,6 +5,8 @@ const Nodecaf = require('nodecaf');

const { addContainer, parseEvent, destroyLoggers } = require('./container');
const { closeTransports } = require('./transport');
const { createTasks } = require('./task');
const { closeTransports, TRANSPORTS } = require('./transport');
const { buildPipelineFunction } = require('./pipeline');
const docker = require('./docker');
const MATCHERS = { stack: 1, services: 1 };
module.exports = () => new Nodecaf({

@@ -15,4 +17,13 @@ conf: __dirname + '/default.toml',

global.transports = {};
global.tasks = {};
global.matcher = {};
for(const job of conf.pipeline){
const type = Object.keys(job)[0];
if(type in MATCHERS)
global.matcher[type] = job[type];
else if(type in TRANSPORTS)
break;
}
global.levels = { trace: 0, debug: 1, info: 2, warn: 3, error: 4, fatal: 5 };
global.docker = docker(conf.docker);

@@ -22,3 +33,3 @@ global.events = await global.docker.events(parseEvent.bind(this));

await createTasks.call(this);
global.processEntry = await buildPipelineFunction.call(this);

@@ -28,7 +39,11 @@ const info = await global.docker.call('/info');

if(conf.healthRedis){
if(typeof conf.health == 'object'){
global.healthRedis = await redis(typeof conf.healthRedis == 'string'
? conf[conf.healthRedis] : conf.healthRedis);
if(typeof conf.health.$inherit == 'string'){
conf.health = { ...conf[conf.health.$inherit], ...conf.health };
delete conf.health.$inherit;
}
global.healthRedis = await redis(conf.health);
global.healthRedis.set('eavesdocker:instance:' + global.node.id,

@@ -35,0 +50,0 @@ global.node.name, 'EX', '60');

const assert = require('assert');
function buildTemplater(template){
const parts = template.split(/{[a-zA-Z0-9_\-\.]+}/g);
const vars = (template.match(/(?<={)[a-zA-Z0-9_\-\.]+(?=})/g) || []).map(v =>
v.split('.').map(ident => `['${ident}']`).join());
let fn = 'return ';
for(let i = 0; i < vars.length; i++)
fn += `'${parts.shift()}' + d${vars.shift()} + `;
fn += `'${parts.shift()}'`;
return new Function('d', fn);
}
const TRANSPORTS = {

@@ -67,3 +81,2 @@

const lp = conf.labelPrefix || '';
const url = conf.url || 'https://gitlab.com';

@@ -93,2 +106,5 @@ const path = url + '/api/v4/projects/' + encodeURIComponent(conf.project) + '/issues';

const labelers = conf.labels.map(buildTemplater);
const titler = buildTemplater(conf.title || '{level} - {message}');
return {

@@ -100,14 +116,6 @@ close: Function.prototype,

const labels = [];
for(const f of conf.labelFields || [])
if(typeof data[f] == 'string' && data[f].length < 32)
labels.push(lp + data[f]);
const title =
(data.level ? data.level.toUpperCase() + ': ' : '') +
data.message || data.msg || 'Important Event'
const { status } = await pool.post(path, headers, {
// assignee_id:
title, labels, 'created_at': new Date(),
title: titler(data),
labels: labelers.map(l => l(data)),
'created_at': new Date(),
description: markdownObject.call(ctx, data)

@@ -126,4 +134,2 @@ });

const defaultSubject = 'Important log entry';
if(!conf.to)

@@ -151,2 +157,4 @@ throw new Error('Recipient not defined');

const subjecter = buildTemplater(conf.subject || '{level} - {message}');
return {

@@ -157,3 +165,3 @@ close: () => transporter.close(),

to: conf.to,
subject: conf.subject || data[conf.subjectField] || defaultSubject,
subject: subjecter(data),
text: JSON.stringify(data, null, 4),

@@ -163,37 +171,63 @@ html: htmlObject(data),

};
}
},
};
twilio(conf){
const { Pool } = require('muhb');
const pool = new Pool({ size: 100, timeout: 4000 });
const url = conf.fakeHost
? `http://${conf.sid}:${conf.token}@${conf.fakeHost}`
: `https://${conf.sid}:${conf.token}@api.twilio.com/2010-04-01/Accounts/${conf.sid}/Messages`;
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
async function createTransport(name, spec, type){
this.global.transports[name] = await TRANSPORTS[type].call(this, spec);
this.log.debug('Installed transport \'%s\' (%s)', name, type);
}
function textObject(obj){
let data = '';
for(const key in obj){
data += ' '.repeat(this.indentation) + key + ': ';
function transform(container, task, input){
let output = { ...input };
if(obj[key] instanceof Date)
data += obj[key].toISOString();
if(typeof obj[key] == 'object'){
this.indentation += 2;
data += '\n' + textObject.call(this, obj[key]);
this.indentation -= 2;
}
else
data += String(obj[key]);
if(typeof task.time == 'string'){
const date = new Date(input[task.time]);
output[task.time] = isNaN(date) ? new Date() : date;
}
data += '\n';
}
return data;
}
if(task.includeSource)
output.source = {
stack: container.stack,
service: container.service,
id: container.id
return {
close: Function.prototype,
push: async data => {
const ctx = { indentation: 0 };
for(const to of conf.to){
const body = textObject.call(ctx, data).substr(0, 1200);
pool.post(url, headers,
`From=${conf.from}&To=${encodeURIComponent(to)}&Body=${encodeURIComponent(body)}`);
}
await pool.done();
}
}
}
if(typeof task.envelope == 'string')
output = { [task.envelope]: output };
};
if(typeof task.apply == 'object')
Object.assign(output, task.apply);
async function createTransport(spec, type){
const name = type + Object.keys(this.global.transports).length;
return output;
if(typeof spec.$inherit == 'string'){
spec = { ...this.conf[spec.$inherit], ...spec };
delete spec.$inherit;
}
this.global.transports[name] = await TRANSPORTS[type].call(this, spec);
this.log.debug('Installed transport \'%s\' (%s)', name, type);
return name;
}
const LEVELS = { trace: 0, debug: 1, info: 2, warn: 3, error: 4, fatal: 5, unlimited: 50 };
module.exports = {

@@ -219,14 +253,4 @@

for(const task of container.tasks){
if(task.level && json.level
&& LEVELS[json.level] >= LEVELS[task.level.from]
&& LEVELS[json.level] <= LEVELS[task.level.to])
continue;
const input = transform(container, task, json);
for(const transport of task.transports)
Promise.resolve(transport.push(input)).catch(err =>
this.log.error({ err }));
}
Promise.resolve(this.global.processEntry(json, container)).catch(err =>
this.log.error({ err }));
},

@@ -233,0 +257,0 @@

{
"name": "eavesdocker",
"version": "0.2.0",
"version": "0.3.0",
"main": "lib/main.js",

@@ -11,3 +11,3 @@ "scripts": {

"mongodb": "^3.6.11",
"muhb": "^3.0.4",
"muhb": "^3.0.5",
"nodecaf": "^0.11.9",

@@ -17,3 +17,4 @@ "nodecaf-redis": "0.0.3",

"toml": "^3.0.0",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"yaml": "^1.10.2"
},

@@ -20,0 +21,0 @@ "devDependencies": {

@@ -34,3 +34,4 @@ // var wtf = require('wtfnode');

token: 'bar',
url: 'http://localhost:12345'
url: 'http://localhost:12345',
labels: [ 'level::{level}', 'barbar' ]
},

@@ -42,8 +43,11 @@ emit: { event: 'foobar' }

async function sleep(ms){
await new Promise(done => setTimeout(done, ms));
}
before(async function(){
this.timeout(10e3);
docker = new Docker(DEFAULT_CONF.docker);
await docker.pull('mhart/alpine-node:slim-13');
await docker.pull('hello-world');
await docker.pull('alpine');
await docker.pull('mhart/alpine-node:slim-14');
await sleep(4000);
});

@@ -60,17 +64,11 @@

async function sleep(ms){
await new Promise(done => setTimeout(done, ms));
}
const debugLabels = {
'com.docker.compose.project': 'foobar',
'com.docker.compose.service': 'bazbaz'
};
async function startBlab(){
const container = await docker.createContainer({
Image: 'mhart/alpine-node:slim-13',
Image: 'mhart/alpine-node:slim-14',
Tty: false,
Init: true,
Labels: debugLabels,
Labels: {
'com.docker.compose.project': 'foobar',
'com.docker.compose.service': 'bazbaz'
},
Cmd: [ 'node', '-e', 'setInterval(() => console.log(Date.now()), 600)' ]

@@ -84,6 +82,9 @@ });

const container = await docker.createContainer({
Image: 'mhart/alpine-node:slim-13',
Image: 'mhart/alpine-node:slim-14',
Tty: false,
Init: true,
Labels: debugLabels,
Labels: {
'com.docker.compose.project': 'foobar',
'com.docker.compose.service': 'bazbaz'
},
Cmd: [ 'node', '-e', 'setTimeout(() => console.log(\'28234768\'), 2000);' +

@@ -111,55 +112,12 @@ 'setTimeout(Function.prototype, 10000)']

describe('Task Definition', function(){
describe('Containers', function(){
it('Should not create tasks without a transport', async function(){
await app.setup({ tasks: { bar: {} } });
await app.start();
assert(!app.global.tasks.emit0);
await app.stop();
});
it('Should skip transport with a bad reference', async function(){
await app.setup({ tasks: { bar: { emit: 'none' } } });
await app.start();
assert(!app.global.tasks.bar);
await app.stop();
});
it('Should create defined tasks', async function(){
await app.setup({ tasks: {
bar: { emit: 'emit' },
foo: { emit: { event: 'bazbah' } },
baz: { emit: 'emit' }
} });
await app.start();
assert.strictEqual(Object.keys(app.global.transports).length, 2);
assert(app.global.tasks.bar);
assert(app.global.tasks.foo);
assert(app.global.tasks.baz);
await app.stop();
});
});
describe('Containers & Tasks', function(){
it('Should not attach containers that don\'t match any task', async function(){
this.timeout(8000);
await app.start();
const [ , { id } ] = await docker.run('hello-world');
await sleep(2000);
assert(!(id in app.global.containers));
await app.stop();
});
it('Should attach containers that match at least 1 task', async function(){
it('Should attach containers', async function(){
this.timeout(5000);
await app.setup({ tasks: { bar: { emit: 'emit' } } });
await app.start();
const [ , { id } ] = await docker.run('hello-world', null, null, {
Labels: debugLabels
});
await sleep(300);
assert(id in app.global.containers);
const c = await startWaitForIt();
await sleep(500);
assert(c.id in app.global.containers);
await app.stop();
await c.kill();
});

@@ -170,3 +128,2 @@

const c = await startBlab();
await app.setup({ tasks: { bar: { emit: 'emit' } } });
await app.start();

@@ -179,24 +136,21 @@ await sleep(500);

it('Should not fail when can\'t attach to some container', async function(){
await app.setup({ tasks: { bar: { emit: 'emit' } } });
it('Should forget containers after they die', async function(){
this.timeout(3000);
await app.start();
const [ , { id } ] = await docker.run('hello-world', null, null, {
Labels: debugLabels,
HostConfig: { LogConfig: { type: 'none' } }
});
await sleep(400);
assert(!(id in app.global.containers));
const c = await startWaitForIt();
await c.kill();
await sleep(1000);
assert(!(c.id in app.global.containers));
await app.stop();
});
it('Should forget containers after they die', async function(){
it('Should not attach containers filtered out in the global line', async function(){
this.timeout(8000);
await app.setup({ tasks: { bar: { emit: 'emit' } } });
app.setup({ pipeline: [ { stack: 'none' } ] });
await app.start();
const [ , { id } ] = await docker.run('hello-world', null, null, {
Labels: debugLabels
});
const c = await startWaitForIt();
await sleep(2000);
assert(!(id in app.global.containers));
assert(!(c.id in app.global.containers));
await app.stop();
await c.kill();
});

@@ -215,21 +169,19 @@

it('Should apply keys to log entry', function(done){
this.timeout(9000);
// it('Should apply keys to log entry', function(done){
// this.timeout(9000);
process.once('foobar', function(msg){
assert.strictEqual(msg.test, 'foo');
done();
});
// process.once('foobar', function(msg){
// assert.strictEqual(msg.test, 'foo');
// done();
// });
(async function(){
await app.setup({ tasks: {
foobar: {
emit: 'emit',
apply: { test: 'foo' }
}
} });
await app.start();
c = await startWaitForIt();
})();
});
// (async function(){
// app.setup({ pipeline: [
// { emit: 'foobar' },
// { apply: { test: 'foo' } }
// ] });
// await app.start();
// c = await startWaitForIt();
// })();
// });

@@ -244,8 +196,6 @@ it('Should ensure given key is a datetime', function(done){

(async function(){
await app.setup({ tasks: {
foobar: {
emit: 'emit',
time: 'foo'
}
} });
app.setup({ pipeline: [
{ time: 'foo' },
{ emit: { event: 'foobar' } }
] });
await app.start();

@@ -265,8 +215,6 @@ c = await startWaitForIt();

(async function(){
await app.setup({ tasks: {
foobar: {
emit: 'emit',
includeSource: true
}
} });
app.setup({ pipeline: [
'includeSource',
{ emit: { event: 'foobar' } }
] });
await app.start();

@@ -286,8 +234,6 @@ c = await startWaitForIt();

(async function(){
await app.setup({ tasks: {
foobar: {
emit: 'emit',
envelope: 'final'
}
} });
app.setup({ pipeline: [
{ envelope: 'final' },
{ emit: { event: 'foobar' } }
] });
await app.start();

@@ -304,5 +250,3 @@ c = await startWaitForIt();

this.timeout(8000);
await app.setup({ tasks: {
foobar: { mongo: { url: MONGO_URL }, }
} });
app.setup({ pipeline: [ { mongo: { url: MONGO_URL } } ] });
await app.start();

@@ -328,3 +272,6 @@ const c = await startWaitForIt();

(async function(){
await app.setup({ tasks: { foobar: { redispub: 'redis', stack: 'foobar' } } });
app.setup({ pipeline: [
{ stack: 'foobar' },
{ redispub: { $inherit: 'redis' } }
] });
await app.start();

@@ -349,3 +296,6 @@

await muhb.delete(MC_HOST + '/messages');
await app.setup({ tasks: { foobar: { email: 'smtp', stack: 'foobar' } } });
app.setup({ pipeline: [
{ stack: 'foobar' },
{ email: { $inherit: 'smtp' } }
] });
await app.start();

@@ -360,2 +310,3 @@ const c = await startWaitForIt();

assert.strictEqual(obj.length, 1);
assert.strictEqual(obj[0].subject.indexOf('debug -'), 0);
});

@@ -376,3 +327,5 @@

await app.setup({ tasks: { foobar: { webhook: { url: 'http://localhost:8765/test' } } } });
app.setup({ pipeline: [
{ webhook: { url: 'http://localhost:8765/test' } }
] });
await app.start();

@@ -389,3 +342,3 @@ const c = await startWaitForIt();

it('Should crate a gitlab issue', async function(){
it('Should create a gitlab issue', async function(){
this.timeout(8000);

@@ -402,3 +355,7 @@

assert.strictEqual(params.id, '23489');
JSON.parse(body.toString());
body = JSON.parse(body.toString());
assert.strictEqual(body.labels[0], 'level::debug');
assert.strictEqual(body.labels[1], 'barbar');
assert.strictEqual(body.title.indexOf('debug - '), 0);
gotIt = true;

@@ -412,3 +369,3 @@ res.end();

await app.setup({ tasks: { foobar: { gitlab: 'gitlab' } } });
app.setup({ pipeline: [ { gitlab: { $inherit: 'gitlab' } } ] });

@@ -425,2 +382,40 @@ await app.start();

it('Should send an SMS via Twilio', async function(){
this.timeout(8000);
let gotIt;
const Nodecaf = require('nodecaf');
const s = new Nodecaf({ conf: { port: 12345 }, api: function({ post }){
post('/', ({ headers, body, res }) => {
const [ user, pass ] = Buffer.from(headers.authorization.split(' ')[1], 'base64').toString('ascii').split(':');
assert.strictEqual(user, 'foo');
assert.strictEqual(pass, 'bar');
assert.strictEqual(body.From, '123');
assert.strictEqual(body.To, '123');
assert.strictEqual(body.Body.indexOf('message: '), 0);
gotIt = true;
res.end();
});
} });
await s.start();
app.setup({ pipeline: [ { twilio: {
sid: 'foo', token: 'bar', from: '123', fakeHost: 'localhost:12345',
to: [ '123' ]
} } ] });
await app.start();
const c = await startWaitForIt();
await sleep(5000);
await c.kill();
await app.stop();
await s.stop();
assert(gotIt);
});
});

@@ -435,5 +430,5 @@

await app.setup({
tasks: { foobar: { emit: 'emit' } },
healthRedis: 'redis'
app.setup({
health: { $inherit: 'redis' },
pipeline: [ { emit: { event: 'emit' } } ]
});

@@ -440,0 +435,0 @@ await app.start();

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc