From 6ebedf489a59397e203a34a9cb7f85c8e303142c Mon Sep 17 00:00:00 2001 From: Sten Larsson Date: Tue, 12 Nov 2024 21:40:45 +0100 Subject: [PATCH] feat: add run-task-capacity-provider-strategy input (#661) When you use cluster auto scaling, you must specify `capacityProviderStrategy` and not `launchType`. Unfortunately this is not possible to configure when running a one-off task with this action. This adds a new input `run-task-capacity-provider-strategy` which takes a JSON array of capacity provider strategy items. When this is specified, `run-task-launch-type` will not be applied. --- action.yml | 5 ++++- dist/index.js | 4 +++- index.js | 4 +++- index.test.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index ebc399b9..08b3098c 100644 --- a/action.yml +++ b/action.yml @@ -55,8 +55,11 @@ inputs: run-task-assign-public-IP: description: "Whether the task's elastic network interface receives a public IP address. The default value is DISABLED but will only be applied if run-task-subnets or run-task-security-groups are also set." required: false + run-task-capacity-provider-strategy: + description: 'A JSON array of capacity provider strategy items which should applied when running a task outside of a service. Will default to none.' + required: false run-task-launch-type: - description: "ECS launch type for tasks run outside of a service. Valid values are 'FARGATE' or 'EC2'. Will default to 'FARGATE'." + description: "ECS launch type for tasks run outside of a service. Valid values are 'FARGATE' or 'EC2'. Will default to 'FARGATE'. Will only be applied if run-task-capacity-provider-strategy is not set." required: false run-task-started-by: description: "A name to use for the startedBy tag when running a task outside of a service. Will default to 'GitHub-Actions'." diff --git a/dist/index.js b/dist/index.js index 4a1717e6..6703c8c6 100644 --- a/dist/index.js +++ b/dist/index.js @@ -38,6 +38,7 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSMa const containerOverrides = JSON.parse(core.getInput('run-task-container-overrides', { required: false }) || '[]'); const assignPublicIP = core.getInput('run-task-assign-public-IP', { required: false }) || 'DISABLED'; const tags = JSON.parse(core.getInput('run-task-tags', { required: false }) || '[]'); + const capacityProviderStrategy = JSON.parse(core.getInput('run-task-capacity-provider-strategy', { required: false }) || '[]'); let awsvpcConfiguration = {} @@ -60,7 +61,8 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSMa overrides: { containerOverrides: containerOverrides }, - launchType: launchType, + capacityProviderStrategy: capacityProviderStrategy.length === 0 ? null : capacityProviderStrategy, + launchType: capacityProviderStrategy.length === 0 ? launchType : null, networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration }, enableECSManagedTags: enableECSManagedTags, tags: tags diff --git a/index.js b/index.js index 8e9f90d9..c1c19663 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSMa const containerOverrides = JSON.parse(core.getInput('run-task-container-overrides', { required: false }) || '[]'); const assignPublicIP = core.getInput('run-task-assign-public-IP', { required: false }) || 'DISABLED'; const tags = JSON.parse(core.getInput('run-task-tags', { required: false }) || '[]'); + const capacityProviderStrategy = JSON.parse(core.getInput('run-task-capacity-provider-strategy', { required: false }) || '[]'); let awsvpcConfiguration = {} @@ -54,7 +55,8 @@ async function runTask(ecs, clusterName, taskDefArn, waitForMinutes, enableECSMa overrides: { containerOverrides: containerOverrides }, - launchType: launchType, + capacityProviderStrategy: capacityProviderStrategy.length === 0 ? null : capacityProviderStrategy, + launchType: capacityProviderStrategy.length === 0 ? launchType : null, networkConfiguration: Object.keys(awsvpcConfiguration).length === 0 ? null : { awsvpcConfiguration: awsvpcConfiguration }, enableECSManagedTags: enableECSManagedTags, tags: tags diff --git a/index.test.js b/index.test.js index 2cae5c1b..e07cc984 100644 --- a/index.test.js +++ b/index.test.js @@ -1144,6 +1144,7 @@ describe('Deploy to ECS', () => { expect(mockRunTask).toHaveBeenNthCalledWith(1,{ startedBy: 'GitHub-Actions', cluster: 'default', + capacityProviderStrategy: null, launchType: 'FARGATE', taskDefinition: 'task:def:arn', overrides: {"containerOverrides": []}, @@ -1185,6 +1186,7 @@ describe('Deploy to ECS', () => { expect(mockRunTask).toHaveBeenCalledWith({ startedBy: 'someJoe', cluster: 'somecluster', + capacityProviderStrategy: null, launchType: 'EC2', taskDefinition: 'task:def:arn', overrides: { containerOverrides: [{ name: 'someapp', command: 'somecmd' }] }, @@ -1195,6 +1197,48 @@ describe('Deploy to ECS', () => { expect(core.setOutput).toHaveBeenNthCalledWith(2, 'run-task-arn', ["arn:aws:ecs:fake-region:account_id:task/arn"]); }); + test('run task with capacity provider strategy', async () => { + core.getInput = jest + .fn() + .mockReturnValueOnce('task-definition.json') // task-definition + .mockReturnValueOnce('') // service + .mockReturnValueOnce('somecluster') // cluster + .mockReturnValueOnce('') // wait-for-service-stability + .mockReturnValueOnce('') // wait-for-minutes + .mockReturnValueOnce('') // force-new-deployment + .mockReturnValueOnce('') // desired-count + .mockReturnValueOnce('false') // enable-ecs-managed-tags + .mockReturnValueOnce('') // propagate-tags + .mockReturnValueOnce('true') // run-task + .mockReturnValueOnce('false') // wait-for-task-stopped + .mockReturnValueOnce('someJoe') // run-task-started-by + .mockReturnValueOnce('') // run-task-launch-type + .mockReturnValueOnce('a,b') // run-task-subnet-ids + .mockReturnValueOnce('c,d') // run-task-security-group-ids + .mockReturnValueOnce(JSON.stringify([{ name: 'someapp', command: 'somecmd' }])) // run-task-container-overrides + .mockReturnValueOnce('') // run-task-assign-public-IP + .mockReturnValueOnce('[{"key": "project", "value": "myproject"}]') // run-task-tags + .mockReturnValueOnce('[{"capacityProvider":"FARGATE_SPOT","weight":1}]'); // run-task-capacity-provider-strategy + + await run(); + expect(core.setFailed).toHaveBeenCalledTimes(0); + + expect(mockEcsRegisterTaskDef).toHaveBeenNthCalledWith(1, { family: 'task-def-family' }); + expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn'); + expect(mockRunTask).toHaveBeenCalledWith({ + startedBy: 'someJoe', + cluster: 'somecluster', + capacityProviderStrategy: [{"capacityProvider":"FARGATE_SPOT","weight":1}], + launchType: null, + taskDefinition: 'task:def:arn', + overrides: { containerOverrides: [{ name: 'someapp', command: 'somecmd' }] }, + networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } }, + enableECSManagedTags: false, + tags: [{"key": "project", "value": "myproject"}], + }); + expect(core.setOutput).toHaveBeenNthCalledWith(2, 'run-task-arn', ["arn:aws:ecs:fake-region:account_id:task/arn"]); + }); + test('run task and service ', async () => { core.getInput = jest .fn() @@ -1236,6 +1280,7 @@ describe('Deploy to ECS', () => { startedBy: 'someJoe', cluster: 'somecluster', taskDefinition: 'task:def:arn', + capacityProviderStrategy: null, launchType: 'EC2', overrides: { containerOverrides: [{ name: 'someapp', command: 'somecmd' }] }, networkConfiguration: { awsvpcConfiguration: { subnets: ['a', 'b'], securityGroups: ['c', 'd'], assignPublicIp: "DISABLED" } }, @@ -1296,6 +1341,7 @@ describe('Deploy to ECS', () => { startedBy: 'someJoe', cluster: 'somecluster', taskDefinition: 'task:def:arn', + capacityProviderStrategy: null, launchType: 'EC2', overrides: { containerOverrides: [] }, networkConfiguration: null,