From c6f3dfbdd9990283e47c9e0cf46393ea13f1541b Mon Sep 17 00:00:00 2001 From: Leonardo Silveira Date: Wed, 6 Sep 2023 15:14:41 -0300 Subject: [PATCH] feat: ability to configure log driver and log options (#255) * hability to configure log driver * changes from code review * changes from code review --- README.md | 19 +++++++++++++++ action.yml | 6 +++++ index.js | 32 ++++++++++++++++++++++--- index.test.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 117 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d955608d..0cb45cf7 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,25 @@ input of the second: cluster: my-cluster ``` +Use the following approach to configure your log driver if needed: + +```yaml + - name: Render Amazon ECS task definition + id: render-web-container + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: task-definition.json + container-name: web + image: amazon/amazon-ecs-sample:latest + log-configuration-log-driver: awslogs + log-configuration-log-options: | + awslogs-create-group=true + awslogs-group=/ecs/web + awslogs-region=us-east-1 + awslogs-stream-prefix=ecs + +``` + See [action.yml](action.yml) for the full documentation for this action's inputs and outputs. ## License Summary diff --git a/action.yml b/action.yml index e1a613c9..ff4b9ce6 100644 --- a/action.yml +++ b/action.yml @@ -16,6 +16,12 @@ inputs: environment-variables: description: 'Variables to add to the container. Each variable is of the form KEY=value, you can specify multiple variables with multi-line YAML strings.' required: false + log-configuration-log-driver: + description: "Create/Override logDriver inside logConfiguration" + required: false + log-configuration-options: + description: "Create/Override options inside logConfiguration. Each variable is of the form key=value, you can specify multiple variables with multi-line YAML strings." + required: false outputs: task-definition: description: 'The path to the rendered task definition file' diff --git a/index.js b/index.js index 99bb717d..8be46604 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,9 @@ async function run() { const environmentVariables = core.getInput('environment-variables', { required: false }); + const logConfigurationLogDriver = core.getInput("log-configuration-log-driver", { required: false }); + const logConfigurationOptions = core.getInput("log-configuration-options", { required: false }); + // Parse the task definition const taskDefPath = path.isAbsolute(taskDefinitionFile) ? taskDefinitionFile : @@ -25,7 +28,7 @@ async function run() { if (!Array.isArray(taskDefContents.containerDefinitions)) { throw new Error('Invalid task definition format: containerDefinitions section is not present or is not an array'); } - const containerDef = taskDefContents.containerDefinitions.find(function(element) { + const containerDef = taskDefContents.containerDefinitions.find(function (element) { return element.name == containerName; }); if (!containerDef) { @@ -50,7 +53,7 @@ async function run() { const separatorIdx = trimmedLine.indexOf("="); // If there's nowhere to split if (separatorIdx === -1) { - throw new Error(`Cannot parse the environment variable '${trimmedLine}'. Environment variable pairs must be of the form NAME=value.`); + throw new Error(`Cannot parse the environment variable '${trimmedLine}'. Environment variable pairs must be of the form NAME=value.`); } // Build object const variable = { @@ -70,6 +73,29 @@ async function run() { }) } + if (logConfigurationLogDriver) { + if (!containerDef.logConfiguration) { containerDef.logConfiguration = {} } + const validDrivers = ["json-file", "syslog", "journald", "logentries", "gelf", "fluentd", "awslogs", "splunk", "awsfirelens"]; + if (!validDrivers.includes(logConfigurationLogDriver)) { + throw new Error(`'${logConfigurationLogDriver}' is invalid logConfigurationLogDriver. valid options are ${validDrivers}. More details: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html`) + } + containerDef.logConfiguration.logDriver = logConfigurationLogDriver + } + + if (logConfigurationOptions) { + if (!containerDef.logConfiguration) { containerDef.logConfiguration = {} } + if (!containerDef.logConfiguration.options) { containerDef.logConfiguration.options = {} } + logConfigurationOptions.split("\n").forEach(function (option) { + option = option.trim(); + if (option && option.length) { // not a blank line + if (option.indexOf("=") == -1) { + throw new Error(`Can't parse logConfiguration option ${option}. Must be in key=value format, one per line`); + } + const [key, value] = option.split("="); + containerDef.logConfiguration.options[key] = value + } + }) + } // Write out a new task definition file var updatedTaskDefFile = tmp.fileSync({ @@ -92,5 +118,5 @@ module.exports = run; /* istanbul ignore next */ if (require.main === module) { - run(); + run(); } diff --git a/index.test.js b/index.test.js index 56c23296..e02fade2 100644 --- a/index.test.js +++ b/index.test.js @@ -71,7 +71,7 @@ describe('Render task definition', () => { postfix: '.json', keep: true, discardDescriptor: true - }); + }); expect(fs.writeFileSync).toHaveBeenNthCalledWith(1, 'new-task-def-file-name', JSON.stringify({ family: 'task-def-family', @@ -129,7 +129,7 @@ describe('Render task definition', () => { postfix: '.json', keep: true, discardDescriptor: true - }); + }); expect(fs.writeFileSync).toHaveBeenNthCalledWith(1, 'new-task-def-file-name', JSON.stringify({ family: 'task-def-family', @@ -150,6 +150,67 @@ describe('Render task definition', () => { expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition', 'new-task-def-file-name'); }); + test('renders logConfiguration on the task definition', async () => { + core.getInput = jest + .fn() + .mockReturnValueOnce('task-definition.json') + .mockReturnValueOnce('web') + .mockReturnValueOnce('nginx:latest') + .mockReturnValueOnce('FOO=bar\nHELLO=world') + .mockReturnValueOnce('awslogs') + .mockReturnValueOnce(`awslogs-create-group=true\nawslogs-group=/ecs/web\nawslogs-region=us-east-1\nawslogs-stream-prefix=ecs`); + + await run() + + expect(tmp.fileSync).toHaveBeenNthCalledWith(1, { + tmpdir: '/home/runner/work/_temp', + prefix: 'task-definition-', + postfix: '.json', + keep: true, + discardDescriptor: true + }); + + + expect(fs.writeFileSync).toHaveBeenNthCalledWith(1, 'new-task-def-file-name', + JSON.stringify({ + family: 'task-def-family', + containerDefinitions: [ + { + name: "web", + image: "nginx:latest", + environment: [ + { + name: "FOO", + value: "bar" + }, + { + name: "DONT-TOUCH", + value: "me" + }, + { + name: "HELLO", + value: "world" + } + ], + logConfiguration: { + logDriver: "awslogs", + options: { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/web", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "ecs" + } + } + }, + { + name: "sidecar", + image: "hello" + } + ] + }, null, 2) + ); + }); + test('error returned for missing task definition file', async () => { fs.existsSync.mockReturnValue(false); core.getInput = jest