Skip to content

Commit d359f41

Browse files
authored
Merge pull request #1 from filipemacedo/fix/use-the-existing-task-definition
Fix/use the existing task definition
2 parents 37ec59d + 48637fd commit d359f41

File tree

4 files changed

+126
-17
lines changed

4 files changed

+126
-17
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ If you do not wish to store your task definition as a file in your git repositor
5656
aws ecs describe-task-definition --task-definition my-task-definition-family --query taskDefinition > task-definition.json
5757
```
5858

59+
If you don't want to create new revisions of your task definition, you can define the task definition family and revision as inputs for the action. By default, the action will use the latest revision of the task definition family if the revision is not specified.
60+
61+
```yaml
62+
- name: Deploy to Amazon ECS
63+
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
64+
with:
65+
task-definition: my-task-definition-family
66+
service: my-service
67+
cluster: my-cluster
68+
wait-for-service-stability: true
69+
```
70+
5971
### Task definition container image values
6072

6173
It is highly recommended that each time your GitHub Actions workflow runs and builds a new container image for deployment, a new container image ID is generated. For example, use the commit ID as the new image's tag, instead of updating the 'latest' tag with the new image. Using a unique container image ID for each deployment allows rolling back to a previous container image.

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ branding:
55
color: 'orange'
66
inputs:
77
task-definition:
8-
description: 'The path to the ECS task definition file to register'
8+
description: 'The path to the ECS task definition file to register or the name of the task definition family to use. If the task definition family is given, the action will use the latest ACTIVE revision of the task definition.'
99
required: true
1010
service:
1111
description: 'The name of the ECS service to deploy to. The action will only register the task definition if no service is given.'

index.js

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -269,23 +269,50 @@ async function run() {
269269
const forceNewDeployInput = core.getInput('force-new-deployment', { required: false }) || 'false';
270270
const forceNewDeployment = forceNewDeployInput.toLowerCase() === 'true';
271271

272-
// Register the task definition
273-
core.debug('Registering the task definition');
274272
const taskDefPath = path.isAbsolute(taskDefinitionFile) ?
275-
taskDefinitionFile :
276-
path.join(process.env.GITHUB_WORKSPACE, taskDefinitionFile);
277-
const fileContents = fs.readFileSync(taskDefPath, 'utf8');
278-
const taskDefContents = maintainValidObjects(removeIgnoredAttributes(cleanNullKeys(yaml.parse(fileContents))));
279-
let registerResponse;
280-
try {
281-
registerResponse = await ecs.registerTaskDefinition(taskDefContents).promise();
282-
} catch (error) {
283-
core.setFailed("Failed to register task definition in ECS: " + error.message);
284-
core.debug("Task definition contents:");
285-
core.debug(JSON.stringify(taskDefContents, undefined, 4));
286-
throw(error);
273+
taskDefinitionFile :
274+
path.join(process.env.GITHUB_WORKSPACE, taskDefinitionFile);
275+
276+
const isExistingTaskDef = fs.existsSync(taskDefPath);
277+
278+
let taskDefArn;
279+
280+
if (!isExistingTaskDef) {
281+
core.debug(`Searching for task definition ${taskDefinitionFile} in ECS`);
282+
try {
283+
const describeResponse = await ecs.describeTaskDefinition({
284+
taskDefinition: taskDefinitionFile
285+
}).promise();
286+
const taskDef = describeResponse.taskDefinition;
287+
288+
if (!taskDef) {
289+
throw new Error(`Task definition ${taskDefinitionFile} not found in ECS`);
290+
}
291+
292+
core.debug(`Found task definition ${taskDef.taskDefinitionArn}`);
293+
taskDefArn = taskDef.taskDefinitionArn;
294+
} catch (error) {
295+
core.setFailed("Failed to describe task definition in ECS: " + error.message);
296+
throw(error);
297+
}
287298
}
288-
const taskDefArn = registerResponse.taskDefinition.taskDefinitionArn;
299+
300+
if (isExistingTaskDef) {
301+
core.debug('Registering the task definition');
302+
const fileContents = fs.readFileSync(taskDefPath, 'utf8');
303+
const taskDefContents = maintainValidObjects(removeIgnoredAttributes(cleanNullKeys(yaml.parse(fileContents))));
304+
let registerResponse;
305+
try {
306+
registerResponse = await ecs.registerTaskDefinition(taskDefContents).promise();
307+
} catch (error) {
308+
core.setFailed("Failed to register task definition in ECS: " + error.message);
309+
core.debug("Task definition contents:");
310+
core.debug(JSON.stringify(taskDefContents, undefined, 4));
311+
throw(error);
312+
}
313+
taskDefArn = registerResponse.taskDefinition.taskDefinitionArn;
314+
}
315+
289316
core.setOutput('task-definition-arn', taskDefArn);
290317

291318
// Update the service with the new task definition

index.test.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ jest.mock('@actions/core');
77
jest.mock('fs', () => ({
88
promises: { access: jest.fn() },
99
readFileSync: jest.fn(),
10+
existsSync: jest.fn().mockReturnValue(true)
1011
}));
1112

1213
const mockEcsRegisterTaskDef = jest.fn();
1314
const mockEcsUpdateService = jest.fn();
1415
const mockEcsDescribeServices = jest.fn();
1516
const mockEcsWaiter = jest.fn();
17+
const mockEcsDescribeTaskDef = jest.fn();
1618
const mockCodeDeployCreateDeployment = jest.fn();
1719
const mockCodeDeployGetDeploymentGroup = jest.fn();
1820
const mockCodeDeployWaiter = jest.fn();
@@ -27,7 +29,8 @@ jest.mock('aws-sdk', () => {
2729
registerTaskDefinition: mockEcsRegisterTaskDef,
2830
updateService: mockEcsUpdateService,
2931
describeServices: mockEcsDescribeServices,
30-
waitFor: mockEcsWaiter
32+
waitFor: mockEcsWaiter,
33+
describeTaskDefinition: mockEcsDescribeTaskDef
3134
})),
3235
CodeDeploy: jest.fn(() => ({
3336
createDeployment: mockCodeDeployCreateDeployment,
@@ -86,6 +89,14 @@ describe('Deploy to ECS', () => {
8689
};
8790
});
8891

92+
mockEcsDescribeTaskDef.mockImplementation(() => {
93+
return {
94+
promise() {
95+
return Promise.resolve({ taskDefinition: { taskDefinitionArn: 'task:def:arn' } });
96+
}
97+
};
98+
})
99+
89100
mockEcsUpdateService.mockImplementation(() => {
90101
return {
91102
promise() {
@@ -151,6 +162,65 @@ describe('Deploy to ECS', () => {
151162
});
152163
});
153164

165+
test("try to get existing task definition ARN when user provides family instead of file", async () => {
166+
fs.existsSync.mockReturnValueOnce(false);
167+
168+
core.getInput = jest
169+
.fn()
170+
.mockReturnValueOnce('task-def-family')
171+
.mockReturnValueOnce('service-456')
172+
.mockReturnValueOnce('cluster-789');
173+
174+
await run();
175+
176+
expect(core.setFailed).toHaveBeenCalledTimes(0);
177+
expect(mockEcsDescribeTaskDef).toHaveBeenNthCalledWith(1, {
178+
taskDefinition: "task-def-family"
179+
});
180+
expect(mockEcsRegisterTaskDef).not.toHaveBeenCalled();
181+
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn');
182+
expect(mockEcsDescribeServices).toHaveBeenNthCalledWith(1, {
183+
cluster: 'cluster-789',
184+
services: ['service-456']
185+
});
186+
expect(mockEcsUpdateService).toHaveBeenNthCalledWith(1, {
187+
cluster: 'cluster-789',
188+
service: 'service-456',
189+
taskDefinition: 'task:def:arn',
190+
forceNewDeployment: false
191+
});
192+
expect(mockEcsWaiter).toHaveBeenCalledTimes(0);
193+
expect(core.info).toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=fake-region#/clusters/cluster-789/services/service-456/events");
194+
});
195+
196+
test("should be able to throw an error when the task definition family is not found", async () => {
197+
fs.existsSync.mockReturnValueOnce(false);
198+
199+
core.getInput = jest
200+
.fn()
201+
.mockReturnValueOnce('task-def-family')
202+
.mockReturnValueOnce('service-456')
203+
.mockReturnValueOnce('cluster-789');
204+
205+
mockEcsDescribeTaskDef.mockImplementation(() => {
206+
return {
207+
promise() {
208+
return Promise.resolve({ taskDefinition: null });
209+
}
210+
};
211+
});
212+
213+
await run()
214+
215+
expect(mockEcsDescribeTaskDef).toHaveBeenNthCalledWith(1, {
216+
taskDefinition: "task-def-family"
217+
});
218+
expect(core.setFailed).toHaveBeenCalledTimes(2);
219+
expect(mockEcsRegisterTaskDef).not.toHaveBeenCalled();
220+
expect(core.setOutput).not.toHaveBeenNthCalledWith(1, 'task-definition-arn', 'task:def:arn');
221+
expect(core.info).not.toBeCalledWith("Deployment started. Watch this deployment's progress in the Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=fake-region#/clusters/cluster-789/services/service-456/events");
222+
});
223+
154224
test('registers the task definition contents and updates the service', async () => {
155225
await run();
156226
expect(core.setFailed).toHaveBeenCalledTimes(0);

0 commit comments

Comments
 (0)