diff --git a/README.md b/README.md index 53681726f..5d57d586e 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,21 @@ The task definition file can be updated prior to deployment with the new contain wait-for-service-stability: true ``` +### Tags + +To turn on [Amazon ECS-managed tags](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-using-tags.html#managed-tags) `aws:ecs:serviceName` and `aws:ecs:clusterName` for the tasks in the service or the standalone tasks by setting `enable-ecs-managed-tags`: + +```yaml + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v2 + with: + task-definition: task-definition.json + service: my-service + cluster: my-cluster + wait-for-service-stability: true + enable-ecs-managed-tags: true +``` + ## Credentials and Region This action relies on the [default behavior of the AWS SDK for Javascript](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html) to determine AWS credentials and region. diff --git a/action.yml b/action.yml index 29ecf22fb..9e9444ef3 100644 --- a/action.yml +++ b/action.yml @@ -64,6 +64,9 @@ inputs: wait-for-task-stopped: description: 'Whether to wait for the task to stop when running it outside of a service. Will default to not wait.' required: false + enable-ecs-managed-tags: + description: "Determines whether to turn on Amazon ECS managed tags 'aws:ecs:serviceName' and 'aws:ecs:clusterName' for the tasks in the service." + required: false outputs: task-definition-arn: description: 'The ARN of the registered ECS task definition' diff --git a/dist/index.js b/dist/index.js index dee4c63db..c0bdd2be4 100644 --- a/dist/index.js +++ b/dist/index.js @@ -27,7 +27,7 @@ const IGNORED_TASK_DEFINITION_ATTRIBUTES = [ ]; // Method to run a stand-alone task with desired inputs -async function runTask(ecs, clusterName, taskDefArn, waitForMinutes) { +async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSManagedTags) { core.info('Running task') const waitForTask = core.getInput('wait-for-task-stopped', { required: false }) || 'false'; @@ -60,7 +60,8 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes) { containerOverrides: containerOverrides }, launchType: launchType, - networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration } + networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration }, + enableECSManagedTags: enableECSManagedTags }); core.debug(`Run task response ${JSON.stringify(runTaskResponse)}`) @@ -134,14 +135,15 @@ async function tasksExitCode(ecs, clusterName, taskArns) { } // Deploy to a service that uses the 'ECS' deployment controller -async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount) { +async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount, enableECSManagedTags) { core.debug('Updating the service'); let params = { cluster: clusterName, service: service, taskDefinition: taskDefArn, - forceNewDeployment: forceNewDeployment + forceNewDeployment: forceNewDeployment, + enableECSManagedTags: enableECSManagedTags }; // Add the desiredCount property only if it is defined and a number. @@ -397,6 +399,8 @@ async function run() { const forceNewDeployInput = core.getInput('force-new-deployment', { required: false }) || 'false'; const forceNewDeployment = forceNewDeployInput.toLowerCase() === 'true'; const desiredCount = parseInt((core.getInput('desired-count', {required: false}))); + const enableECSManagedTagsInput = core.getInput('enable-ecs-managed-tags', { required: false }) || 'false'; + const enableECSManagedTags = enableECSManagedTagsInput.toLowerCase() === 'true'; // Register the task definition core.debug('Registering the task definition'); @@ -424,7 +428,7 @@ async function run() { core.debug(`shouldRunTask: ${shouldRunTask}`); if (shouldRunTask) { core.debug("Running ad-hoc task..."); - await runTask(ecs, clusterName, taskDefArn, waitForMinutes); + await runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSManagedTags); } // Update the service with the new task definition @@ -448,7 +452,7 @@ async function run() { if (!serviceResponse.deploymentController || !serviceResponse.deploymentController.type || serviceResponse.deploymentController.type === 'ECS') { // Service uses the 'ECS' deployment controller, so we can call UpdateService core.debug('Updating service...'); - await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount); + await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount, enableECSManagedTags); } else if (serviceResponse.deploymentController.type === 'CODE_DEPLOY') { // Service uses CodeDeploy, so we should start a CodeDeploy deployment diff --git a/index.js b/index.js index 8a7027330..6168cc706 100644 --- a/index.js +++ b/index.js @@ -21,7 +21,7 @@ const IGNORED_TASK_DEFINITION_ATTRIBUTES = [ ]; // Method to run a stand-alone task with desired inputs -async function runTask(ecs, clusterName, taskDefArn, waitForMinutes) { +async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSManagedTags) { core.info('Running task') const waitForTask = core.getInput('wait-for-task-stopped', { required: false }) || 'false'; @@ -54,7 +54,8 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes) { containerOverrides: containerOverrides }, launchType: launchType, - networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration } + networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration }, + enableECSManagedTags: enableECSManagedTags }); core.debug(`Run task response ${JSON.stringify(runTaskResponse)}`) @@ -128,14 +129,15 @@ async function tasksExitCode(ecs, clusterName, taskArns) { } // Deploy to a service that uses the 'ECS' deployment controller -async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount) { +async function updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount, enableECSManagedTags) { core.debug('Updating the service'); let params = { cluster: clusterName, service: service, taskDefinition: taskDefArn, - forceNewDeployment: forceNewDeployment + forceNewDeployment: forceNewDeployment, + enableECSManagedTags: enableECSManagedTags }; // Add the desiredCount property only if it is defined and a number. @@ -391,6 +393,8 @@ async function run() { const forceNewDeployInput = core.getInput('force-new-deployment', { required: false }) || 'false'; const forceNewDeployment = forceNewDeployInput.toLowerCase() === 'true'; const desiredCount = parseInt((core.getInput('desired-count', {required: false}))); + const enableECSManagedTagsInput = core.getInput('enable-ecs-managed-tags', { required: false }) || 'false'; + const enableECSManagedTags = enableECSManagedTagsInput.toLowerCase() === 'true'; // Register the task definition core.debug('Registering the task definition'); @@ -418,7 +422,7 @@ async function run() { core.debug(`shouldRunTask: ${shouldRunTask}`); if (shouldRunTask) { core.debug("Running ad-hoc task..."); - await runTask(ecs, clusterName, taskDefArn, waitForMinutes); + await runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSManagedTags); } // Update the service with the new task definition @@ -442,7 +446,7 @@ async function run() { if (!serviceResponse.deploymentController || !serviceResponse.deploymentController.type || serviceResponse.deploymentController.type === 'ECS') { // Service uses the 'ECS' deployment controller, so we can call UpdateService core.debug('Updating service...'); - await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount); + await updateEcsService(ecs, clusterName, service, taskDefArn, waitForService, waitForMinutes, forceNewDeployment, desiredCount, enableECSManagedTags); } else if (serviceResponse.deploymentController.type === 'CODE_DEPLOY') { // Service uses CodeDeploy, so we should start a CodeDeploy deployment diff --git a/index.test.js b/index.test.js index 95bb51330..2b610cd74 100644 --- a/index.test.js +++ b/index.test.js @@ -182,7 +182,8 @@ describe('Deploy to ECS', () => { cluster: 'cluster-789', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(waitUntilServicesStable).toHaveBeenCalledTimes(0); expect(core.info).toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://fake-region.console.aws.amazon.com/ecs/v2/clusters/cluster-789/services/service-456/events?region=fake-region"); @@ -213,7 +214,8 @@ describe('Deploy to ECS', () => { cluster: 'cluster-789', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(waitUntilServicesStable).toHaveBeenCalledTimes(0); expect(core.info).toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://fake-region.console.aws.amazon.com/ecs/v2/clusters/cluster-789/services/service-456/events?region=fake-region"); @@ -708,6 +710,7 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // run-task .mockReturnValueOnce('') // desired count + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('/hello/appspec.json') // codedeploy-appspec .mockReturnValueOnce('MyApplication') // codedeploy-application .mockReturnValueOnce('MyDeploymentGroup'); // codedeploy-deployment-group @@ -944,7 +947,8 @@ describe('Deploy to ECS', () => { cluster: 'cluster-789', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(waitUntilServicesStable).toHaveBeenNthCalledWith( 1, @@ -983,7 +987,8 @@ describe('Deploy to ECS', () => { cluster: 'cluster-789', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(waitUntilServicesStable).toHaveBeenNthCalledWith( 1, @@ -1022,7 +1027,8 @@ describe('Deploy to ECS', () => { cluster: 'cluster-789', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(waitUntilServicesStable).toHaveBeenNthCalledWith( 1, @@ -1063,7 +1069,8 @@ describe('Deploy to ECS', () => { desiredCount: 4, service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: true + forceNewDeployment: true, + enableECSManagedTags: false }); }); @@ -1087,7 +1094,8 @@ describe('Deploy to ECS', () => { cluster: 'default', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); }); @@ -1113,6 +1121,7 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('') // cluster .mockReturnValueOnce('') // wait-for-service-stability .mockReturnValueOnce('') // wait-for-minutes + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count .mockReturnValueOnce('true'); // run-task @@ -1129,7 +1138,8 @@ describe('Deploy to ECS', () => { launchType: 'FARGATE', taskDefinition: 'task:def:arn', overrides: {"containerOverrides": []}, - networkConfiguration: null + networkConfiguration: null, + enableECSManagedTags: false }); expect(core.setOutput).toHaveBeenNthCalledWith(2, 'run-task-arn', ["arn:aws:ecs:fake-region:account_id:task/arn"]); @@ -1145,6 +1155,7 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('') // wait-for-minutes .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count + .mockReturnValueOnce('true') // enable-ecs-managed-tags .mockReturnValueOnce('true') // run-task .mockReturnValueOnce('false') // wait-for-task-stopped .mockReturnValueOnce('someJoe') // run-task-started-by @@ -1164,7 +1175,8 @@ describe('Deploy to ECS', () => { launchType: 'EC2', taskDefinition: 'task:def:arn', overrides: { containerOverrides: [{ name: 'someapp', command: 'somecmd' }] }, - networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } } + networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } }, + enableECSManagedTags: true }); expect(core.setOutput).toHaveBeenNthCalledWith(2, 'run-task-arn', ["arn:aws:ecs:fake-region:account_id:task/arn"]); }); @@ -1177,8 +1189,9 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('somecluster') // cluster .mockReturnValueOnce('true') // wait-for-service-stability .mockReturnValueOnce('') // wait-for-minutes - .mockReturnValueOnce('') // force-new-deployment + .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('true') // run-task .mockReturnValueOnce('false') // wait-for-task-stopped .mockReturnValueOnce('someJoe') // run-task-started-by @@ -1200,7 +1213,8 @@ describe('Deploy to ECS', () => { cluster: 'somecluster', service: 'service-456', taskDefinition: 'task:def:arn', - forceNewDeployment: false + forceNewDeployment: false, + enableECSManagedTags: false }); expect(mockRunTask).toHaveBeenCalledWith({ startedBy: 'someJoe', @@ -1208,7 +1222,8 @@ describe('Deploy to ECS', () => { taskDefinition: 'task:def:arn', launchType: 'EC2', overrides: { containerOverrides: [{ name: 'someapp', command: 'somecmd' }] }, - networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } } + networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } }, + enableECSManagedTags: false }); expect(core.setOutput).toHaveBeenNthCalledWith(2, 'run-task-arn', ["arn:aws:ecs:fake-region:account_id:task/arn"]); }); @@ -1223,6 +1238,7 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('') // wait-for-minutes .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('true') // run-task .mockReturnValueOnce('true'); // wait-for-task-stopped @@ -1244,6 +1260,7 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('somecluster') // cluster .mockReturnValueOnce('true') // wait-for-service-stability .mockReturnValueOnce('') // wait-for-minutes + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count .mockReturnValueOnce('true') // run-task @@ -1262,7 +1279,8 @@ describe('Deploy to ECS', () => { taskDefinition: 'task:def:arn', launchType: 'EC2', overrides: { containerOverrides: [] }, - networkConfiguration: null + networkConfiguration: null, + enableECSManagedTags: false }); }); @@ -1317,8 +1335,9 @@ describe('Deploy to ECS', () => { .mockReturnValueOnce('') // wait-for-minutes .mockReturnValueOnce('') // force-new-deployment .mockReturnValueOnce('') // desired-count + .mockReturnValueOnce('') // enable-ecs-managed-tags .mockReturnValueOnce('true') // run-task - .mockReturnValueOnce('false'); // wait-for-task-stopped + .mockReturnValueOnce('false'); // wait-for-task-stopped mockRunTask.mockImplementation( () => Promise.resolve({ @@ -1427,4 +1446,4 @@ describe('Deploy to ECS', () => { expect(core.setFailed).toHaveBeenNthCalledWith(1, 'Failed to register task definition in ECS: Could not parse'); expect(core.setFailed).toHaveBeenNthCalledWith(2, 'Could not parse'); }); -}); +}); \ No newline at end of file