diff --git a/.eslintrc.json b/.eslintrc.json index 8c6a277..eb6c782 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 8, "sourceType": "script" }, "rules": { diff --git a/index.js b/index.js index 8ce4752..eb58ddd 100644 --- a/index.js +++ b/index.js @@ -74,6 +74,10 @@ module.exports = function (options, cb) { return taskRunner.runPromisified(params); }) .then((taskDefinition) => { + if (taskDefinition.failures) { + throw new Error("ECS RunTask returned failure messages", { cause: taskDefinition.failures }); + } + const taskArn = taskDefinition.tasks[0].taskArn; const taskId = taskArn.substring(taskArn.lastIndexOf('/') + 1); const formatter = new FormatStream(); diff --git a/test/format-transform-stream.js b/test/format-transform-stream.js index 95611df..a62de23 100644 --- a/test/format-transform-stream.js +++ b/test/format-transform-stream.js @@ -1,6 +1,6 @@ 'use strict' -const expect = require('expect.js'); +const expect = require('expect.js'); const FormatTransformStream = require('../lib/format-transform-stream'); describe('FormatTransformStream', function() { diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..69e2b52 --- /dev/null +++ b/test/index.js @@ -0,0 +1,112 @@ +'use strict' + +const { mockClient } = require('aws-sdk-client-mock'); +const { ECS, DescribeTaskDefinitionCommand, RunTaskCommand } = require("@aws-sdk/client-ecs"); +const { CloudWatchLogs, GetLogEventsCommand } = require("@aws-sdk/client-cloudwatch-logs"); +const expect = require('expect.js'); +const { promisify } = require('node:util'); +const index = promisify(require('../index')); + +describe('index', function () { + let cwlMock; + let ecsMock; + beforeEach(() => { + cwlMock = mockClient(CloudWatchLogs); + ecsMock = mockClient(ECS); + }); + + it('should do the thing', async function () { + const options = { + taskDefinitionArn: 'task-definition.arn', + containerName: 'meow' + }; + + ecsMock.on(DescribeTaskDefinitionCommand).callsFake(async params => { + expect(params.taskDefinition).to.eql(options.taskDefinitionArn) + + return { + taskDefinition: { + taskDefinitionArn: options.taskDefinitionArn, + containerDefinitions: [{ + name: options.containerName, + logConfiguration: { + logDriver: 'awslogs', + options: { 'awslogs-group': '', 'awslogs-stream-prefix': '' }, + } + }], + } + } + }); + + // thread the randon EOF through to kill the stream + let eofSet; + const eof = new Promise(r => { + eofSet = r; + }) + ecsMock.on(RunTaskCommand).callsFake(async params => { + expect(params.taskDefinition).to.eql(options.taskDefinitionArn) + + // Grab the `xxx` from ..."TASK FINISHED: $(echo -n xxx | base64),"... + eofSet(params.overrides.containerOverrides[0].command[2].split(' ')[9]) + + return { + tasks: [{ + taskArn: '' + }] + } + }); + + cwlMock.on(GetLogEventsCommand).callsFake(async _params => { + return { + events: [{ + timestamp: 1477346285562, + message: Buffer.from(await eof).toString('base64'), + }] + }; + }); + + // if this returns without crashing we're gucci + return index(options); + }); + + + it('should warn us when a task fails to launch', async function () { + const options = { + taskDefinitionArn: 'task-definition.arn', + containerName: 'meow' + }; + + ecsMock.on(DescribeTaskDefinitionCommand).callsFake(async params => { + expect(params.taskDefinition).to.eql(options.taskDefinitionArn) + + return { + taskDefinition: { + taskDefinitionArn: options.taskDefinitionArn, + containerDefinitions: [{ + name: options.containerName, + logConfiguration: { + logDriver: 'awslogs', + options: { 'awslogs-group': '', 'awslogs-stream-prefix': '' }, + } + }], + } + } + }); + + const reason = 'ECS is haunted' + ecsMock.on(RunTaskCommand).callsFake(async params => { + expect(params.taskDefinition).to.eql(options.taskDefinitionArn) + + return { + failures: [{ reason }] + } + }); + + try { + await index(options); + expect().fail("App should have thrown an error about ECS returning errors") + } catch (err) { + expect(err.cause[0].reason).to.eql(reason) + } + }); +}); diff --git a/test/log-stream.js b/test/log-stream.js index f92c9eb..3261288 100644 --- a/test/log-stream.js +++ b/test/log-stream.js @@ -7,8 +7,10 @@ const LogsStream = require('../lib/log-stream'); describe('LogStream', function () { this.timeout(5000); - const cwlMock = mockClient(CloudWatchLogs); - afterEach(() => { cwlMock.reset(); }); + let cwlMock ; + beforeEach(() => { + cwlMock = mockClient(CloudWatchLogs); + }); const options = { logGroup: 'yee', diff --git a/test/taskrunner.js b/test/taskrunner.js index 26f9ed0..5bfa70b 100644 --- a/test/taskrunner.js +++ b/test/taskrunner.js @@ -1,7 +1,7 @@ 'use strict' const { mockClient } = require('aws-sdk-client-mock'); -const { ECS, StartTaskCommand, StopTaskCommand, RunTaskCommand } = require("@aws-sdk/client-ecs"); +const { ECS, StopTaskCommand, RunTaskCommand } = require("@aws-sdk/client-ecs"); const expect = require('expect.js'); const taskRunner = require('../lib/taskrunner'); @@ -21,8 +21,10 @@ describe('TaskRunner', function () { }) describe('#run', function () { - const ecsMock = mockClient(ECS); - afterEach(() => { ecsMock.reset(); }); + let ecsMock; + beforeEach(() => { + ecsMock = mockClient(ECS); + }); it('should make a call to AWS.ECS with correct arguments not including env', function (done) { const options = { @@ -83,7 +85,7 @@ describe('TaskRunner', function () { describe('#stop', function () { const ecsMock = mockClient(ECS); - afterEach(() => { ecsMock.reset(); }); + beforeEach(() => { ecsMock.reset(); }); it('should make a call to AWS.ECS with correct arguments', function (done) { const options = {